Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 8343e92

Browse files
Merge pull request #558 from chakra-ui/develop
release @chakra-ui/[email protected]
2 parents 786efc2 + 74618e9 commit 8343e92

File tree

8 files changed

+308
-20
lines changed

8 files changed

+308
-20
lines changed

Diff for: .changeset/big-icons-report.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'nuxt-js': patch
3+
'@chakra-ui/vue': patch
4+
'@chakra-ui/nuxt': patch
5+
'@chakra-ui/theme-vue': patch
6+
'chakra-ui-docs': patch
7+
---
8+
9+
Fixes skip link and textarea props

Diff for: packages/chakra-ui-core/src/CSkipNav/CSkipNav.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Hey! Welcome to @chakra-ui/vue SkipNavLink
3+
*
4+
* Renders a link that remains hidden until focused to skip to the main content.
5+
*
6+
* @see Docs https://vue.chakra-ui.com/skip-nav-link
7+
* @see Source https://github.com/chakra-ui/chakra-ui-vue/blob/master/packages/chakra-ui-core/src/CSkipNav/CSkipNav.js
8+
*/
9+
10+
import { SNA } from '../config/props.types'
11+
import { createStyledAttrsMixin, mode } from '../utils'
12+
import CBox from '../CBox'
13+
14+
const FALLBACK_ID = 'chakra-skip-nav'
15+
16+
const createSkipNavLinkStyles = (props) => {
17+
const baseStyles = {
18+
userSelect: 'none',
19+
border: '0',
20+
borderRadius: 'md',
21+
fontWeight: 'semibold',
22+
height: '1px',
23+
width: '1px',
24+
margin: '-1px',
25+
padding: '0',
26+
outline: '0',
27+
overflow: 'hidden',
28+
position: 'absolute',
29+
clip: 'rect(0 0 0 0)',
30+
_focus: {
31+
clip: 'auto',
32+
width: 'auto',
33+
height: 'auto',
34+
boxShadow: 'outline',
35+
padding: '1rem',
36+
position: 'fixed',
37+
top: '1.5rem',
38+
insetStart: '1.5rem',
39+
bg: mode('white', 'gray.700')
40+
}
41+
}
42+
43+
return { ...baseStyles }
44+
}
45+
46+
/**
47+
* CSkipNavLink component
48+
*
49+
* Renders a link that remains hidden until focused to skip to the main content.
50+
*
51+
* @see Docs https://vue.chakra-ui.com/skip-nav
52+
*/
53+
const CSkipNavLink = {
54+
name: 'CSkipNavLink',
55+
mixins: [createStyledAttrsMixin('CSkipNavLink')],
56+
props: {
57+
id: {
58+
type: String,
59+
default: FALLBACK_ID
60+
}
61+
},
62+
computed: {
63+
colorMode () {
64+
return this.$chakraColorMode()
65+
},
66+
theme () {
67+
return this.$chakraTheme()
68+
},
69+
componentStyles () {
70+
return createSkipNavLinkStyles()
71+
}
72+
},
73+
render (h) {
74+
return h(
75+
'a',
76+
{
77+
class: this.className,
78+
attrs: {
79+
href: `#${this.id}`
80+
}
81+
},
82+
this.$slots.default
83+
)
84+
}
85+
}
86+
87+
/**
88+
* CSkipNavLink component
89+
*
90+
* Renders a div as the target for the link.
91+
*
92+
* @see Docs https://vue.chakra-ui.com/skip-nav
93+
*/
94+
const CSkipNavContent = {
95+
name: 'CSkipNavContent',
96+
mixins: [createStyledAttrsMixin('CSkipNavContent')],
97+
props: {
98+
id: {
99+
type: String,
100+
default: FALLBACK_ID
101+
},
102+
to: SNA
103+
},
104+
render (h) {
105+
return h(
106+
CBox,
107+
{
108+
class: this.className,
109+
attrs: {
110+
id: this.id,
111+
tabIndex: '-1',
112+
style: {
113+
outline: 0
114+
},
115+
'data-testid': 'chakra-skip-nav'
116+
}
117+
},
118+
this.$slots.default
119+
)
120+
}
121+
}
122+
123+
export { CSkipNavLink, CSkipNavContent }
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { storiesOf } from '@storybook/vue'
2+
import CInput from '../CInput'
3+
import CText from '../CText'
4+
import CList, { CListItem, CListIcon } from '../CList'
5+
import { CSkipNavLink, CSkipNavContent } from './CSkipNav'
6+
7+
storiesOf('UI | SkipNav', module).add('Default', () => ({
8+
components: {
9+
CSkipNavLink,
10+
CSkipNavContent,
11+
CInput,
12+
CText,
13+
CList,
14+
CListItem,
15+
CListIcon
16+
},
17+
template: `
18+
<div>
19+
<CSkipNavLink>Skip to Content</CSkipNavLink>
20+
<CSkipNavContent>
21+
<main>
22+
<CText>
23+
To test the SkipNav Components:
24+
<CList mb="4">
25+
<CListItem>
26+
<CListIcon icon="chevron-right" />
27+
Place focus on the input
28+
</CListItem>
29+
<CListItem>
30+
<CListIcon icon="chevron-right" />
31+
Press "Shift + Tab" to make the SkipNavLink appear
32+
</CListItem>
33+
<CListItem>
34+
<CListIcon icon="chevron-right" />
35+
Hit "Enter". You might leave the page to load up the iFrame isolated
36+
</CListItem>
37+
<CListItem>
38+
<CListIcon icon="chevron-right" />
39+
You should now see a blue outline over all the content.
40+
</CListItem>
41+
</CList>
42+
</CText>
43+
<label>Example Form Search</label>
44+
<CInput placeholder="Search" />
45+
</main>
46+
</CSkipNavContent>
47+
</div>
48+
`
49+
}))
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Skip Nav | Accessibility ♿️
2+
3+
This report is adapted to the outline from the [WAI-ARIA Authoring Patterns practices](https://www.w3.org/WAI/ARIA/apg/patterns/) and technique information from [WebAIM](https://webaim.org/techniques/skipnav/), supported by Chakra UI for the `CSkipNav` components.
4+
5+
### Description
6+
7+
The Skip Navigation components are a tandem used to provide interaction for the keyboard user in skipping navigation content (or redundant content used at the top of multiple pages) to the main body of the page.
8+
9+
#### Components
10+
11+
`@chakra-ui/vue` exports 2 Skip Nav related components:
12+
13+
- `CSkipNavLink`
14+
- `CSkipNavContent`
15+
16+
### `CSkipNav` Keyboard Interaction
17+
18+
- **`Tab`**:
19+
- On initial load of the page, moves focus to the `CSkipNavLink` element, provided that this component is the first focusable element in the page.
20+
- On focus of the `CSkipNavContent` component, moves to the next focusable element inside the wrapper.
21+
- **`Enter`**:
22+
- Moves focus from the `CSkipNavLink` element to the `CSkipNavContent` element.
23+
24+
### `CDrawer` WAI-ARIA Roles, States, and Properties:
25+
26+
- The `CSkipNavLink` contains an href linking to the `id` of the `CSkipNavContent` component.
27+
- The `CSkipNavContent` component renders `tabindex="-1"` to show visible change of focus to the main content. A screen reader is expected to immediately read out the first of this content.
28+
29+
### Consideration of Multiple `CSkipNavLinks` Components
30+
31+
In most cases, a single component is sufficient.
32+
33+
However, a very complex page with several repeated elements may neeeded additional skip links, either by providing the whole set at the very beginning of the page to navigate through, or added as in-page links to allow the user to quickly bypass content, including confusing or inaccessible content such as ASCII art, complex tables, or complex social media feeds.
34+
35+
Remember, the purpose of skip navigation links is to make keyboard navigation more efficient. Adding more links increases link-clutter. At what point will you need to add a "Skip the skip links" link?!
36+
37+
### Concerns with Aestheic Impact
38+
39+
The `CSkipNavLink` component is designed to be hidden until a user navigtes to it with a keyboard. The address concerns of the link being unattractive or confusing to users who do not need it.
40+
41+
Techniques like `display: none` or the `hidden` attribute will remove the component from keyboard interaction. Therefore, the component is styled in such a way that it positioned out of the visible browser window, and then on focus with CSS it transitions into view.
42+
43+
If there is concern with a user potentially tabbing quickly away from the component, it can be styled or scripted to remain visible for an extended period of time.
44+
45+
### `CLink` WAI-ARIA compliance
46+
47+
- [WCAG 2.4.1 (Bypass Blocks - Level A)](https://www.w3.org/TR/WCAG21/#bypass-blocks): This component tandem is a mechanism bypassing blocks of content that are repeated on multiple pages.
48+
- The `CSkipNavLink` component renders an `<a>` element with rendered visibility as hidden off the screen. When tabbing to the link, it becomes visible for sighted keyboard users, and read out by a screen reader.
49+
- With the `CSkipNavContent` component containing `tabindex="-1"`, the component renders a focus ring on focus for the visual keyboard user to indicate arrival to the main content.
50+
51+
Noticed a bug or inconsistency with this component? [Open an issue](https://github.com/chakra-ui/chakra-ui-vue/issues/new/choose)

Diff for: packages/chakra-ui-core/src/CSkipNav/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './CSkipNav'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { CSkipNavLink, CSkipNavContent } from '../CSkipNav'
2+
import { fireEvent, render, userEvent, screen, wait } from '@/tests/test-utils'
3+
4+
const renderComponent = (props) => {
5+
const base = {
6+
components: { CSkipNavLink, CSkipNavContent },
7+
template: `
8+
<div>
9+
<CSkipNavLink>Skip to Content</CSkipNavLink>
10+
<CSkipNavContent>
11+
<main>
12+
<form>
13+
<input type="text" placeholder="Search" />
14+
</form>
15+
</main>
16+
</CSkipNavContent>
17+
</div>
18+
`,
19+
...props
20+
}
21+
return render(base)
22+
}
23+
24+
const getSkipLink = () => screen.getByText('Skip to Content')
25+
26+
const getContentWrapper = () => screen.getByTestId('chakra-skip-nav')
27+
28+
const triggerSkipLink = async () => {
29+
const link = getSkipLink()
30+
await fireEvent.keyDown(link, {
31+
key: 'Enter',
32+
code: 'Enter'
33+
})
34+
}
35+
36+
describe('CSkipNav', () => {
37+
beforeEach(async () => {
38+
renderComponent()
39+
await userEvent.tab()
40+
})
41+
42+
it('should be tabbed to link after initial render', () => {
43+
const link = getSkipLink()
44+
expect(link).toHaveAttribute('href', '#chakra-skip-nav')
45+
})
46+
47+
it('should navigate to content wrapper on selecting skip link', async () => {
48+
await triggerSkipLink()
49+
const contentWrapper = getContentWrapper()
50+
51+
wait(() => {
52+
expect(contentWrapper).toHaveFocus()
53+
})
54+
})
55+
56+
it('should tab to input after wrapper focus', async () => {
57+
await triggerSkipLink()
58+
await userEvent.tab()
59+
60+
const input = screen.getByPlaceholderText('Search')
61+
62+
expect(input).toHaveFocus()
63+
})
64+
})

Diff for: packages/chakra-ui-core/src/CTextarea/CTextarea.js

+1-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { inputProps } from '../CInput/utils/input.props'
1212
import { forwardProps, extractListeners } from '../utils'
1313

1414
import CInput from '../CInput'
15-
import { SNA } from '../config/props.types'
1615

1716
/**
1817
* CTextarea component
@@ -31,23 +30,7 @@ const CTextarea = {
3130
},
3231
props: {
3332
...inputProps,
34-
inputValue: String,
35-
py: {
36-
type: SNA,
37-
default: '8px'
38-
},
39-
minHeight: {
40-
type: SNA,
41-
default: '80px'
42-
},
43-
fontFamily: {
44-
type: SNA,
45-
default: 'body'
46-
},
47-
lineHeight: {
48-
type: SNA,
49-
default: 'shorter'
50-
}
33+
inputValue: String
5134
},
5235
render (h, { props, slots, data, listeners, ...rest }) {
5336
// Default styles

Diff for: packages/chakra-ui-core/src/index.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export { default as CRadioButtonGroup } from './CRadioButtonGroup'
9999
// S
100100
export { default as CSimpleGrid } from './CSimpleGrid'
101101
export { default as CSelect } from './CSelect'
102+
export * from './CSkipNav'
102103
export { default as CSlider } from './CSlider'
103104
export * from './CSlider'
104105
export { default as CSpinner } from './CSpinner'
@@ -124,8 +125,15 @@ export { default as defaultTheme } from '@chakra-ui/theme-vue'
124125

125126
// Internal icons
126127
export { parsePackIcons } from './utils/icons'
127-
export { mode, colorModeObserver as localColorModeObserver, defineColorModeObserver } from './utils/color-mode-observer'
128+
export {
129+
mode,
130+
colorModeObserver as localColorModeObserver,
131+
defineColorModeObserver
132+
} from './utils/color-mode-observer'
128133
export { default as internalIcons } from './lib/internal-icons'
129134

130135
// Directives
131-
export { createServerDirective, createClientDirective } from './directives/chakra.directive'
136+
export {
137+
createServerDirective,
138+
createClientDirective
139+
} from './directives/chakra.directive'

0 commit comments

Comments
 (0)