-
Notifications
You must be signed in to change notification settings - Fork 22
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
fix(ktabs): allow tab anchors to be links [KHCP-13866] #2532
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -397,7 +397,7 @@ const matchingLineNumbers = ref<number[]>([]) | |
const currentLineIndex = ref<null | number>(null) | ||
const totalLines = computed((): number[] => Array.from({ length: props.code?.split('\n').length }, (_, index) => index + 1)) | ||
const maxLineNumberWidth = computed((): string => totalLines.value[totalLines.value.length - 1]?.toString().length + 'ch') | ||
const maxLineNumberWidth = computed((): string => totalLines.value[totalLines.value?.length - 1]?.toString().length + 'ch') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👋 Drive-by comment: Is this a bug fix, and if so should it go in a different PR? If not should it be here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a bug fix. Since it's such a small change I think it's fine if it goes in in the same PR |
||
const linePrefix = computed((): string => props.id.toLowerCase().replace(/\s+/g, '-')) | ||
const isProcessing = computed((): boolean => props.processing || isProcessingInternally.value) | ||
const isShowingFilteredCode = computed((): boolean => isFilterMode.value && filteredCode.value !== '') | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,21 +11,24 @@ | |
class="tab-item" | ||
:class="{ active: activeTab === tab.hash }" | ||
> | ||
<div | ||
<component | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if this is something you'll want to address in this PR, but if all of the "tabs" are using anchors, it means its essentially a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call, added a dynamic role for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you are using the Question: Would you consider a very simple I would guess a theoretical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is getting out of scope for the PR. TBH the existing aria-label should be removed as it's generic and doesn't actually describe the content. "Tabs" doesn't label the type of content being displayed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, but would be really good to make sure all of the native anchor functionality and accessibility is also maintained if you are sticking to this component. I believe we wrote something about us being accessibility focussed somewhere? 👀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that KNavigation is out of scope for this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I still think out of scope for this PR. Can create a ticket to discuss for a future update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok but if we are using Whilst discussing this (and this might also be out of scope so let me know if so), I think you might want to remove some of these for when "hide-panels" is true also, as in that case its not really a Let me know the link for that ticket @adamdehaven and I'll catch up with that tomorrow 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is getting out of scope of this PR. I will back out the change related to |
||
:is="tabComponent(tab).tag" | ||
:aria-controls="hidePanels ? undefined : `panel-${tab.hash}`" | ||
:aria-selected="hidePanels ? undefined : (activeTab === tab.hash ? 'true' : 'false')" | ||
class="tab-link" | ||
:class="{ 'has-panels': !hidePanels, disabled: tab.disabled }" | ||
:class="{ disabled: tab.disabled }" | ||
role="tab" | ||
:tabindex="getAnchorTabindex(tab)" | ||
v-bind="tabComponent(tab).attributes" | ||
@click="!tab.disabled ? handleTabChange(tab.hash) : undefined" | ||
@click.prevent="!tab.disabled ? handleTabChange(tab.hash) : undefined" | ||
@keydown.enter.prevent="!tab.disabled ? handleTabChange(tab.hash) : undefined" | ||
@keydown.space.prevent="!tab.disabled ? handleTabChange(tab.hash) : undefined" | ||
> | ||
<slot :name="`${getTabSlotName(tab.hash)}-anchor`"> | ||
<span>{{ tab.title }}</span> | ||
adamdehaven marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</slot> | ||
</div> | ||
</component> | ||
</li> | ||
</ul> | ||
|
||
|
@@ -49,7 +52,7 @@ | |
|
||
<script lang="ts" setup> | ||
import type { PropType } from 'vue' | ||
import { ref, watch } from 'vue' | ||
import { computed, ref, watch } from 'vue' | ||
import type { Tab } from '@/types' | ||
|
||
const props = defineProps({ | ||
|
@@ -75,6 +78,9 @@ | |
type: Boolean, | ||
default: false, | ||
}, | ||
/** | ||
* @deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you mark as deprecated in the docs as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed this prop from docs (our standard practice for deprecated props is to remove them from Kongponents docs). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well maybe we can't use a word "standard" here since we have different patterns:
So how do we want to go about this one? In my opinion, we don't want to promote usage of it so we should remove it from docs. |
||
*/ | ||
anchorTabindex: { | ||
type: Number, | ||
default: 0, | ||
|
@@ -105,6 +111,18 @@ | |
return typeof props.anchorTabindex === 'number' && props.anchorTabindex >= -1 && props.anchorTabindex <= 32767 ? String(props.anchorTabindex) : '0' | ||
} | ||
|
||
const tabComponent = (tab: Tab) => { | ||
if (tab.to) { | ||
if (typeof tab.to === 'string') { | ||
return { tag: 'a', attributes: { href: tab.disabled ? undefined : tab.to } } | ||
} else if (typeof tab.to === 'object') { | ||
return { tag: 'router-link', attributes: { to: tab.disabled ? undefined : tab.to } } | ||
} | ||
} | ||
|
||
return { tag: 'div', attributes: {} } | ||
} | ||
|
||
watch(() => props.modelValue, (newTabHash) => { | ||
activeTab.value = newTabHash | ||
emit('change', newTabHash) | ||
|
@@ -125,11 +143,7 @@ | |
ul { | ||
border-bottom: var(--kui-border-width-10, $kui-border-width-10) solid var(--kui-color-border, $kui-color-border); | ||
display: flex; | ||
font-family: var(--kui-font-family-text, $kui-font-family-text); | ||
font-size: var(--kui-font-size-30, $kui-font-size-30); | ||
font-weight: var(--kui-font-weight-semibold, $kui-font-weight-semibold); | ||
gap: var(--kui-space-40, $kui-space-40); | ||
line-height: var(--kui-line-height-40, $kui-line-height-40); | ||
list-style: none; | ||
margin-bottom: var(--kui-space-70, $kui-space-70); | ||
margin-top: var(--kui-space-0, $kui-space-0); | ||
|
@@ -148,30 +162,23 @@ | |
white-space: nowrap; | ||
|
||
.tab-link { | ||
@include defaultButtonReset; | ||
align-items: center; | ||
|
||
border-radius: var(--kui-border-radius-30, $kui-border-radius-30); | ||
color: var(--kui-color-text-neutral, $kui-color-text-neutral); | ||
cursor: pointer; | ||
display: inline-flex; | ||
font-family: var(--kui-font-family-text, $kui-font-family-text); | ||
font-size: var(--kui-font-size-30, $kui-font-size-30); | ||
font-weight: var(--kui-font-weight-semibold, $kui-font-weight-semibold); | ||
gap: var(--kui-space-40, $kui-space-40); | ||
line-height: var(--kui-line-height-40, $kui-line-height-40); | ||
padding: var(--kui-space-30, $kui-space-30) var(--kui-space-50, $kui-space-50); | ||
text-decoration: none; | ||
transition: color $kongponentsTransitionDurTimingFunc, background-color $kongponentsTransitionDurTimingFunc, box-shadow $kongponentsTransitionDurTimingFunc; | ||
user-select: none; | ||
|
||
// Applies the padding to the tab’s content when not showing panels which is typically used for placing links inside KTabs for navigational tabs. Otherwise, clicking the tab outside of the link’s box will mark it as active but won’t actually navigate. | ||
&.has-panels, | ||
&:not(.has-panels) :deep(> *) { | ||
padding: var(--kui-space-30, $kui-space-30) var(--kui-space-50, $kui-space-50); | ||
} | ||
|
||
a, :deep(a) { | ||
color: var(--kui-color-text-neutral, $kui-color-text-neutral); | ||
text-decoration: none; | ||
|
||
&:focus-visible { | ||
@include kTabsFocus; | ||
} | ||
} | ||
|
||
&:hover:not(.disabled) { | ||
background-color: var(--kui-color-background-neutral-weaker, $kui-color-background-neutral-weaker); | ||
} | ||
|
@@ -184,6 +191,15 @@ | |
color: var(--kui-color-text-disabled, $kui-color-text-disabled); | ||
cursor: not-allowed; | ||
} | ||
|
||
:slotted(a) { | ||
color: var(--kui-color-text-neutral, $kui-color-text-neutral); | ||
text-decoration: none; | ||
|
||
&:focus-visible { | ||
@include kTabsFocus; | ||
} | ||
} | ||
} | ||
|
||
&.active { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
export interface Tab { | ||
/** Has to be unique, corresponds to the panel slot name */ | ||
hash: string | ||
title: string | ||
disabled?: boolean | ||
/** If present, tab will be rendered as either a router-link or an anchor */ | ||
to?: string | object | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This prop is being removed? This would be a breaking change, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically yes but this prop was added a bit more than a month ago and there is no usage of it in our repos (except for 2 occurrences that I introduced in MFEs but I will fix those up).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still a breaking change. It should be deprecated instead so we don't have to do a breaking release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, let me bring it back and deprecate it instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.