Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

va-skip-link #3720

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/docs/page-config/navigationRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ export const navigationRoutes: NavigationRoute[] = [
name: "sidebar-item",
displayName: "Sidebar Item",
},
{
Roman4437 marked this conversation as resolved.
Show resolved Hide resolved
name: "skip-link",
displayName: "Skip Link",
meta: {
badge: navigationBadge.new('1.7.6'),
}
},
{
name: 'stepper',
displayName: 'Stepper',
Expand Down
11 changes: 11 additions & 0 deletions packages/docs/page-config/ui-elements/skip-link/api-description.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineApiDescription } from "~/modules/page-config/runtime";

export default defineApiDescription({
props: {
target: "Selector to the target component.",
position: "Position on the page.",
},
slots: {
default: "Skip link content slot.",
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default defineManualApi({
slots: {
default: {},
},
});
20 changes: 20 additions & 0 deletions packages/docs/page-config/ui-elements/skip-link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import apiOptions from "./api-options";
import apiDescription from './api-description';

export default definePageConfig({
blocks: [
block.title("Skip Link"),
block.paragraph("`va-skip-link` skip to the target component."),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more text here. You can describe in technical terms what component does - then ask chat gpt to get you some text.


block.subtitle("Basic usage"),

block.paragraph("By default, `va-skip-link` needs a target and position, please keep in mind that the component should be placed as the very first element."),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a short code example here? Just code. Right now it's not clear at all how to use component.


block.subtitle("Example"),

block.paragraph("To test this component - click `Esc`, then press `Tab` and you should be able to see a button that would get you straight to content on `Enter`."),

block.subtitle("API"),
block.api("VaSkipLink", apiDescription, apiOptions),
],
});
23 changes: 23 additions & 0 deletions packages/docs/pages/[page-config-category]/[page].vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<template>
<div class="page-config">
<va-skip-link
v-if="$route.path === '/ui-elements/skip-link'"
class="skip-link"
position="bottom-right"
target="#api"
>
Jump to API
</va-skip-link>
<PageConfig
v-if="!isLoading && config"
:page-config="config"
Expand Down Expand Up @@ -77,4 +85,19 @@ watchEffect(() => {
}
}
}

.skip-link {
padding: 0.5rem;
border-radius: 2px;
outline: none;
color: white;
background-color: rgb(72, 72, 198);
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;

&:hover {
background-color: rgb(94, 94, 222);
}
}
</style>
1 change: 1 addition & 0 deletions packages/nuxt/src/config/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default [
'VaSlider',
'VaSkeleton',
'VaSkeletonGroup',
'VaSkipLink',
'VaSpacer',
'VaSplit',
'VaSwitch',
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"push-production": "npm publish --tag=latest",
"precommit": "lint-staged --concurrent=false && yarn test:unit --run",
"server:webapp": "npx http-server dist --push-state",
"serve:storybook": "storybook dev -p 6006",
"serve:storybook": "storybook dev -p 6006 --no-open",
"build:storybook": "storybook build",
"start:storybook": "serve storybook-static",
"chromatic": "npx chromatic --build-script-name=build:storybook --project-token=chpt_0b6e7d8b20a17cf"
Expand Down Expand Up @@ -188,4 +188,4 @@
]
}
}
}
}
1 change: 1 addition & 0 deletions packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export * from './va-rating'
export * from './va-select'
export * from './va-separator'
export * from './va-skeleton'
export * from './va-skip-link'
export * from './va-sidebar'
export * from './va-slider'
export * from './va-spacer'
Expand Down
53 changes: 53 additions & 0 deletions packages/ui/src/components/va-skip-link/VaSkipLink.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { within } from '@storybook/testing-library'
import { VaRadio } from '../va-radio'
import { VaSkipLink } from './'
Roman4437 marked this conversation as resolved.
Show resolved Hide resolved
import { expect } from '@storybook/jest'

export default {
title: 'VaSkipLink',
component: VaSkipLink,
}

export const Default = () => ({
components: { VaSkipLink },
template: `
<va-skip-link target="#target">
[skip link]
</va-skip-link>
<div :style="{ height: '100dvh' }">
[not target]
</div>
<div
id="target"
:style="{ height: '100dvh' }"
>
[target]
</div>
`,
})

const OPTIONS = ['top-left', 'top-right', 'bottom-left', 'bottom-right']

export const Position = () => ({
Roman4437 marked this conversation as resolved.
Show resolved Hide resolved
components: { VaSkipLink, VaRadio },
data: () => ({ options: OPTIONS, value: OPTIONS[3] }),
template: `
<va-skip-link
target="#target"
:position="value"
>
[{{ value }}]
</va-skip-link>
<va-radio v-model="value" :options="options"/>
`,
})

Position.play = async ({ canvasElement, step }) => {
const canvas = within(canvasElement)
const skipLink = canvas.getByRole('link', { name: '[bottom-right]' }) as HTMLElement

await step('Visible on focus', async () => {
skipLink.focus()
expect(skipLink.style.opacity).not.toEqual(0)
})
}
47 changes: 47 additions & 0 deletions packages/ui/src/components/va-skip-link/VaSkipLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<a
Roman4437 marked this conversation as resolved.
Show resolved Hide resolved
class="va-skip-link"
role="link"
:href="target"
:style="position"
>
<slot />
</a>
</template>

<script lang="ts">
import { PropType, defineComponent } from 'vue'
import { usePosition } from './hooks/usePosition'

export default defineComponent({
name: 'VaSkipLink',
props: {
target: { type: String, default: undefined },
position: {
type: String as PropType<'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'>,
default: 'bottom-right',
validator: (v: string) => ['top-right', 'top-left', 'bottom-right', 'bottom-left'].includes(v),
},
},

setup (props) {
const { position } = usePosition(props)

return { position }
},
})
</script>

<style lang="scss">
.va-skip-link {
display: flex;
position: fixed;
opacity: 0;
pointer-events: none;

&:focus {
opacity: 1;
pointer-events: inherit;
}
}
</style>
20 changes: 20 additions & 0 deletions packages/ui/src/components/va-skip-link/hooks/usePosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { computed } from "vue"

interface SkipLinkProps {
position: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
}

export const usePosition = (props: SkipLinkProps) => {
const position = computed(() => {
const positionMap = {
'top-right': { top: '1rem', right: '1rem' },
'top-left': { top: '1rem', left: '1rem' },
'bottom-right': { bottom: '1rem', right: '1rem' },
'bottom-left': { bottom: '1rem', left: '1rem' },
}

return positionMap[props.position]
})

return { position }
}
4 changes: 4 additions & 0 deletions packages/ui/src/components/va-skip-link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import withConfigTransport from '../../services/config-transport/withConfigTransport'
import _VaSkipLink from './VaSkipLink.vue'

export const VaSkipLink = withConfigTransport(_VaSkipLink)
11 changes: 11 additions & 0 deletions packages/ui/src/components/va-skip-link/tests/VaSkipLink.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import { mountWithGlobalConfig } from '../../../utils/unit-test-utils'

import VaSkipLink from '../VaSkipLink.vue'

describe('VaSkipLink', () => {
it('should render without an error', () => {
const wrapper = mountWithGlobalConfig(VaSkipLink)
expect(wrapper.exists()).toBeTruthy()
})
})
1 change: 1 addition & 0 deletions packages/ui/src/services/vue-plugin/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export {
VaSeparator,
VaSkeleton,
VaSkeletonGroup,
VaSkipLink,
VaSidebar,
VaSidebarItem,
VaSidebarItemContent,
Expand Down