feat: Keyboard accessible property pane header (#11339)

This commit is contained in:
Aswath K 2022-02-24 12:34:31 +05:30 committed by GitHub
parent bd9636f514
commit 2ad121a092
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 18 deletions

View File

@ -378,7 +378,8 @@ const ButtonStyles = css<ThemeProp & ButtonProps>`
fill: ${(props) => btnColorStyles(props, "main").txtColor}; fill: ${(props) => btnColorStyles(props, "main").txtColor};
} }
} }
&:hover { &:hover,
&:focus {
text-decoration: none; text-decoration: none;
background-color: ${(props) => btnColorStyles(props, "hover").bgColor}; background-color: ${(props) => btnColorStyles(props, "hover").bgColor};
color: ${(props) => btnColorStyles(props, "hover").txtColor}; color: ${(props) => btnColorStyles(props, "hover").txtColor};

View File

@ -687,14 +687,6 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
maxHeight={props.dropdownMaxHeight || "auto"} maxHeight={props.dropdownMaxHeight || "auto"}
> >
{options.map((option: DropdownOption, index: number) => { {options.map((option: DropdownOption, index: number) => {
if (renderOption) {
return renderOption({
option,
index,
optionClickHandler,
optionWidth,
});
}
let isSelected = false; let isSelected = false;
if ( if (
props.isMultiSelect && props.isMultiSelect &&
@ -708,6 +700,15 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
isSelected = isSelected =
(props.selected as DropdownOption).value === option.value; (props.selected as DropdownOption).value === option.value;
} }
if (renderOption) {
return renderOption({
option,
index,
optionClickHandler,
optionWidth,
isSelectedNode: isSelected,
});
}
return !option.isSectionHeader ? ( return !option.isSectionHeader ? (
<OptionWrapper <OptionWrapper
aria-selected={isSelected} aria-selected={isSelected}

View File

@ -30,10 +30,11 @@ const StyledDiv = styled.div`
props.theme.spaces[7]}px; props.theme.spaces[7]}px;
margin: ${(props) => props.theme.spaces[2]}px 0px; margin: ${(props) => props.theme.spaces[2]}px 0px;
a:first-child { button:first-child {
margin-top: ${(props) => props.theme.spaces[2]}px; margin-top: ${(props) => props.theme.spaces[2]}px;
width: 100%;
} }
a:nth-child(2) { button:nth-child(2) {
border: none; border: none;
background-color: transparent; background-color: transparent;
text-transform: none; text-transform: none;
@ -43,7 +44,7 @@ const StyledDiv = styled.div`
${(props) => getTypographyByKey(props, "p3")} ${(props) => getTypographyByKey(props, "p3")}
margin-top: ${(props) => props.theme.spaces[2]}px; margin-top: ${(props) => props.theme.spaces[2]}px;
:hover { :hover, :focus {
text-decoration: underline; text-decoration: underline;
} }
} }
@ -95,11 +96,15 @@ function ConnectDataCTA(props: ConnectDataCTAProps) {
category={Category.primary} category={Category.primary}
onClick={onClick} onClick={onClick}
size={Size.large} size={Size.large}
tabIndex={0}
tag="button"
text="CONNECT DATA" text="CONNECT DATA"
/> />
<Button <Button
category={Category.tertiary} category={Category.tertiary}
onClick={openHelpModal} onClick={openHelpModal}
tabIndex={0}
tag="button"
text="Learn more" text="Learn more"
/> />
</StyledDiv> </StyledDiv>

View File

@ -1,4 +1,4 @@
import React, { memo, useMemo, useCallback } from "react"; import React, { memo, useMemo, useCallback, useEffect } from "react";
import styled from "styled-components"; import styled from "styled-components";
import Icon, { IconSize } from "components/ads/Icon"; import Icon, { IconSize } from "components/ads/Icon";
import Dropdown, { import Dropdown, {
@ -125,6 +125,7 @@ const OptionWrapper = styled.div<{ hasError: boolean; fillIconColor: boolean }>`
const OptionContentWrapper = styled.div<{ const OptionContentWrapper = styled.div<{
hasError: boolean; hasError: boolean;
isSelected: boolean;
}>` }>`
padding: ${(props) => props.theme.spaces[2] + 1}px padding: ${(props) => props.theme.spaces[2] + 1}px
${(props) => props.theme.spaces[5]}px; ${(props) => props.theme.spaces[5]}px;
@ -134,6 +135,10 @@ const OptionContentWrapper = styled.div<{
line-height: 8px; line-height: 8px;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
background-color: ${(props) =>
props.isSelected &&
!props.hasError &&
props.theme.colors.dropdown.hovered.bg};
span:first-child { span:first-child {
font-size: 10px; font-size: 10px;
@ -210,7 +215,7 @@ const useDependencyList = (name: string) => {
const dependencyOptions = const dependencyOptions =
entityDependencies?.directDependencies.map((e) => ({ entityDependencies?.directDependencies.map((e) => ({
label: e, label: e,
value: getEntityId(e), value: getEntityId(e) ?? e,
})) ?? []; })) ?? [];
const inverseDependencyOptions = const inverseDependencyOptions =
entityDependencies?.inverseDependencies.map((e) => ({ entityDependencies?.inverseDependencies.map((e) => ({
@ -245,12 +250,28 @@ function OptionNode(props: any) {
}); });
}; };
const handleKeyDown = (e: KeyboardEvent) => {
if (!props.isSelectedNode) return;
if (e.key === " " || e.key === "Enter") onClick();
};
useEffect(() => {
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [props.isSelectedNode]);
return ( return (
<OptionWrapper <OptionWrapper
fillIconColor={!entityInfo?.datasourceName} fillIconColor={!entityInfo?.datasourceName}
hasError={!!entityInfo?.hasError} hasError={!!entityInfo?.hasError}
> >
<OptionContentWrapper hasError={!!entityInfo?.hasError} onClick={onClick}> <OptionContentWrapper
hasError={!!entityInfo?.hasError}
isSelected={props.isSelectedNode}
onClick={onClick}
>
<span>{entityInfo?.icon}</span> <span>{entityInfo?.icon}</span>
<Text type={TextType.H6}> <Text type={TextType.H6}>
{props.option.label}{" "} {props.option.label}{" "}
@ -297,6 +318,7 @@ const TriggerNode = memo((props: TriggerNodeProps) => {
<Tooltip <Tooltip
content={tooltipText} content={tooltipText}
disabled={props.isOpen} disabled={props.isOpen}
openOnTargetFocus={false}
position={props.tooltipPosition} position={props.tooltipPosition}
> >
{props.entityCount ? `${props.entityCount} ${ENTITY}` : "No Entity"} {props.entityCount ? `${props.entityCount} ${ENTITY}` : "No Entity"}
@ -360,7 +382,12 @@ function PropertyPaneConnections(props: PropertyPaneConnectionsProps) {
height={`${CONNECTION_HEIGHT}px`} height={`${CONNECTION_HEIGHT}px`}
options={dependencies.dependencyOptions} options={dependencies.dependencyOptions}
renderOption={(optionProps) => { renderOption={(optionProps) => {
return <OptionNode option={optionProps.option} />; return (
<OptionNode
isSelectedNode={optionProps.isSelectedNode}
option={optionProps.option}
/>
);
}} }}
selected={selectedOption} selected={selectedOption}
showDropIcon={false} showDropIcon={false}

View File

@ -73,7 +73,7 @@ function PropertyPaneView(
tooltipPosition: "bottom-right", tooltipPosition: "bottom-right",
icon: ( icon: (
<button <button
className="p-1 hover:bg-warmGray-100 group t--copy-widget" className="p-1 hover:bg-warmGray-100 focus:bg-warmGray-100 group t--copy-widget"
onClick={onCopy} onClick={onCopy}
> >
<CopyIcon className="w-4 h-4 text-gray-500" /> <CopyIcon className="w-4 h-4 text-gray-500" />
@ -85,7 +85,7 @@ function PropertyPaneView(
tooltipPosition: "bottom-right", tooltipPosition: "bottom-right",
icon: ( icon: (
<button <button
className="p-1 hover:bg-warmGray-100 group t--delete-widget" className="p-1 hover:bg-warmGray-100 focus:bg-warmGray-100 group t--delete-widget"
onClick={onDelete} onClick={onDelete}
> >
<DeleteIcon className="w-4 h-4 text-gray-500" /> <DeleteIcon className="w-4 h-4 text-gray-500" />