Skip to content

Commit

Permalink
fix(layers): fix layers tab wcag (#2443)
Browse files Browse the repository at this point in the history
* fix(layers): fix layers tab wcag  closes2428

* fix(layers): fix layers tab wcag  closes2428

* fix(layers): fix layers tab wcag  closes2428
  • Loading branch information
kaminderpal authored Aug 21, 2024
1 parent 81a6dcd commit afe5f9a
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 67 deletions.
27 changes: 11 additions & 16 deletions packages/geoview-core/src/core/components/layers/layers-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { logger } from '@/core/utils/logger';
import { ResponsiveGridLayout, ResponsiveGridLayoutExposedMethods } from '@/core/components/common/responsive-grid-layout';
import { Typography } from '@/ui/typography/typography';
import { TypeContainerBox } from '@/core/types/global-types';
import { useUIStoreActions } from '@/core/stores/store-interface-and-intial-values/ui-state';
import { TypeLegendLayer } from './types';

interface TypeLayersPanel {
containerType?: TypeContainerBox;
Expand All @@ -21,31 +23,24 @@ export function LayersPanel({ containerType }: TypeLayersPanel): JSX.Element {

const selectedLayer = useSelectedLayer(); // get store value
const displayState = useLayerDisplayState();
const { setSelectedLayerPath } = useLayerStoreActions();
const [isLayoutEnlarged, setIsLayoutEnlarged] = useState<boolean>(false);

const responsiveLayoutRef = useRef<ResponsiveGridLayoutExposedMethods>(null);

/*
// Using helpers
const helpers = useLegendHelpers();
useEffect(() => {
// Log
logger.logTraceUseEffect('LAYERS-PANEL - mount');
const { setSelectedLayerPath } = useLayerStoreActions();
const { setSelectedFooterLayerListItem } = useUIStoreActions();

helpers.populateLegendStoreWithFakeData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
*/
const responsiveLayoutRef = useRef<ResponsiveGridLayoutExposedMethods>(null);

const showLayerDetailsPanel = (): void => {
const showLayerDetailsPanel = (layer: TypeLegendLayer): void => {
responsiveLayoutRef.current?.setIsRightPanelVisible(true);
responsiveLayoutRef.current?.setRightPanelFocus();
// set the focus item when layer item clicked.
setSelectedFooterLayerListItem(`${layer.layerId}`);
};

const leftPanel = (): JSX.Element => {
return (
<Box>
<LeftPanel setIsLayersListPanelVisible={showLayerDetailsPanel} isLayoutEnlarged={isLayoutEnlarged} />
<Box id="layers-left-panel">
<LeftPanel showLayerDetailsPanel={showLayerDetailsPanel} isLayoutEnlarged={isLayoutEnlarged} />
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import React, { ChangeEvent, useEffect, useRef, useState, KeyboardEvent } from 'react';
import { useTranslation } from 'react-i18next';
import { SelectChangeEvent, useTheme } from '@mui/material';
import {
Expand Down Expand Up @@ -84,6 +84,10 @@ export function AddNewLayer(): JSX.Element {
const [stepButtonDisable, setStepButtonDisable] = useState<boolean>(true);

const dragPopover = useRef(null);
const uploadBtnRef = useRef<HTMLButtonElement>(null);
const serviceTypeRef = useRef<HTMLDivElement>(null);
const isMultipleRef = useRef<HTMLDivElement>(null);
const isMultipleTextFieldRef = useRef<HTMLDivElement>(null);

// get values from store
const mapId = useGeoViewMapId();
Expand Down Expand Up @@ -1015,6 +1019,29 @@ export function AddNewLayer(): JSX.Element {
if (activeStep === 2 && layerEntries.length > 0) setStepButtonDisable(false);
}, [layerName, activeStep, layerEntries]);

useEffect(() => {
if (activeStep === 0) {
uploadBtnRef.current?.focus();
}
if (activeStep === 1) {
(serviceTypeRef.current?.getElementsByTagName('input')[0].previousSibling as HTMLDivElement).focus();
}
if (activeStep === 2) {
if (isMultipleRef.current) {
// handle is Multiple fields focus
const id = isMultipleRef.current?.dataset?.id;
const elem = isMultipleRef.current?.querySelector('#service-layer-label') as HTMLInputElement;
if (id === 'autocomplete' && elem) {
elem.focus();
} else {
isMultipleTextFieldRef.current?.getElementsByTagName('input')[0]?.focus();
}
}
}
if (activeStep === 3) {
isMultipleTextFieldRef.current?.getElementsByTagName('input')[0]?.focus();
}
}, [activeStep]);
/**
* Handle file dragged into dropzone
*
Expand Down Expand Up @@ -1069,6 +1096,13 @@ export function AddNewLayer(): JSX.Element {
}
};

const handleKeyDown = (e: KeyboardEvent<HTMLButtonElement>): void => {
if (e.key === 'Enter') {
handleBack();
e.preventDefault();
}
};

/**
* Creates a set of Continue / Back buttons
*
Expand All @@ -1095,7 +1129,14 @@ export function AddNewLayer(): JSX.Element {
{isLast ? t('layers.finish') : t('layers.continue')}
</Button>
{!isFirst && (
<Button variant="contained" className="buttonOutlineFilled" size="small" type="text" onClick={handleBack}>
<Button
variant="contained"
className="buttonOutlineFilled"
size="small"
type="text"
onClick={handleBack}
onKeyDown={(e) => handleKeyDown(e)}
>
{t('layers.back')}
</Button>
)}
Expand Down Expand Up @@ -1168,6 +1209,7 @@ export function AddNewLayer(): JSX.Element {
type="text"
onClick={() => document.getElementById('fileUpload')?.click()}
className="buttonOutlineFilled"
ref={uploadBtnRef}
>
<FileUploadIcon />
<Box component="span">{t('layers.upload')}</Box>
Expand Down Expand Up @@ -1207,6 +1249,7 @@ export function AddNewLayer(): JSX.Element {
inputLabel={{
id: 'service-type-label',
}}
ref={serviceTypeRef}
menuItems={layerOptions.map(([value, label]) => ({
key: value,
item: {
Expand All @@ -1228,7 +1271,13 @@ export function AddNewLayer(): JSX.Element {
children: (
<>
{layerList.length === 0 && (
<TextField label={t('layers.name')} variant="standard" value={layerName} onChange={handleNameLayer} />
<TextField
label={t('layers.name')}
variant="standard"
value={layerName}
onChange={handleNameLayer}
ref={isMultipleTextFieldRef}
/>
)}
{layerList.length > 1 && (layerList[0] as TypeLayerEntryConfig).layerName && (
<Autocomplete
Expand All @@ -1238,15 +1287,18 @@ export function AddNewLayer(): JSX.Element {
disableCloseOnSelect
id="service-layer-label"
options={layerList as TypeLayerEntryConfig[]}
getOptionLabel={(option) => `${option.layerName!.en} (${option.layerId})`}
getOptionLabel={(option) =>
`${(option as TypeLayerEntryConfig).layerName!.en} (${(option as TypeLayerEntryConfig).layerId})`
}
renderOption={(props, option, { selected }) => (
<li {...props}>
<li {...props} key={(option as TypeLayerEntryConfig).layerName!.en}>
<Checkbox icon={uncheckedIcon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />
{option.layerName!.en}
{(option as TypeLayerEntryConfig).layerName!.en}
</li>
)}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange={handleSelectLayer as any}
ref={isMultipleRef}
renderInput={(params) => <TextField {...params} label={t('layers.layerSelect')} />}
/>
)}
Expand All @@ -1257,16 +1309,19 @@ export function AddNewLayer(): JSX.Element {
disableClearable={!isMultiple()}
id="service-layer-label"
options={layerList as TypeGeoviewLayerConfig[]}
getOptionLabel={(option) => `${option.geoviewLayerName!.en} (${option.geoviewLayerId})`}
getOptionLabel={(option) =>
`${(option as TypeGeoviewLayerConfig).geoviewLayerName!.en} (${(option as TypeGeoviewLayerConfig).geoviewLayerId})`
}
disableCloseOnSelect
renderOption={(props, option, { selected }) => (
<li {...props}>
<li {...props} key={(option as TypeGeoviewLayerConfig).geoviewLayerName!.en}>
<Checkbox icon={uncheckedIcon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />
{option.geoviewLayerName!.en}
{(option as TypeGeoviewLayerConfig).geoviewLayerName!.en}
</li>
)}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange={handleSelectLayer as any}
ref={isMultipleRef}
renderInput={(params) => <TextField {...params} label={t('layers.layerSelect')} />}
/>
)}
Expand All @@ -1290,6 +1345,7 @@ export function AddNewLayer(): JSX.Element {
variant="standard"
value={layerName}
onChange={handleNameLayer}
ref={isMultipleTextFieldRef}
/>
<br />
<NavButtons isLast handleNext={handleStepLast} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, KeyboardEvent } from 'react';
import { Box, CircularProgressBase, DeleteOutlineIcon, IconButton, UndoIcon } from '@/ui';
import { TypeLegendLayer } from '@/core/components/layers/types';
import { useLayerStoreActions } from '@/core/stores/store-interface-and-intial-values/layer-state';
import { useMapStoreActions } from '@/core/stores/store-interface-and-intial-values/map-state';
import { logger } from '@/core/utils/logger';
import { LAYER_STATUS } from '@/core/utils/constant';
import { useUIStoreActions } from '@/core/stores/store-interface-and-intial-values/ui-state';

interface DeleteUndoButtonProps {
layer: TypeLegendLayer;
Expand All @@ -13,13 +14,14 @@ interface DeleteUndoButtonProps {
interface UndoButtonProps {
progressValue: number;
onUndo: () => void;
handleKeyDown: (e: KeyboardEvent) => void;
}

function UndoButtonWithProgress(props: UndoButtonProps): JSX.Element {
// Log
logger.logTraceRender('components/layers/left-panel/delete-undo-button/UndoButtonWithProgress');

const { progressValue, onUndo } = props;
const { progressValue, onUndo, handleKeyDown } = props;
return (
<Box sx={{ position: 'relative', display: 'inline-flex' }} onClick={onUndo}>
<CircularProgressBase variant="determinate" size={40} value={progressValue} />
Expand All @@ -35,7 +37,7 @@ function UndoButtonWithProgress(props: UndoButtonProps): JSX.Element {
justifyContent: 'center',
}}
>
<IconButton edge="end" size="small">
<IconButton edge="end" size="small" onKeyDown={(e) => handleKeyDown(e)}>
<UndoIcon />
</IconButton>
</Box>
Expand All @@ -55,6 +57,7 @@ export function DeleteUndoButton(props: DeleteUndoButtonProps): JSX.Element {
// get store actions
const { deleteLayer, setLayerDeleteInProgress, getLayerDeleteInProgress } = useLayerStoreActions();
const { getVisibilityFromOrderedLayerInfo, setOrToggleLayerVisibility } = useMapStoreActions();
const { setSelectedFooterLayerListItem } = useUIStoreActions();

const handleDeleteClick = (): void => {
if (getVisibilityFromOrderedLayerInfo(layer.layerPath)) setOrToggleLayerVisibility(layer.layerPath);
Expand All @@ -68,6 +71,22 @@ export function DeleteUndoButton(props: DeleteUndoButtonProps): JSX.Element {
setLayerDeleteInProgress(false);
};

const handleDeleteKeyDown = (e: KeyboardEvent): void => {
if (e.key === 'Enter') {
e.preventDefault();
handleDeleteClick();
setSelectedFooterLayerListItem(layer.layerId);
}
};

const handleUndoDeleteKeyDown = (e: KeyboardEvent): void => {
if (e.key === 'Enter') {
handleUndoClick();
setSelectedFooterLayerListItem('');
e.preventDefault();
}
};

useEffect(() => {
if (progress === 100) {
deleteLayer(layer.layerPath);
Expand Down Expand Up @@ -96,7 +115,7 @@ export function DeleteUndoButton(props: DeleteUndoButtonProps): JSX.Element {
}
if (!inUndoState && layer.controls?.remove !== false && !getLayerDeleteInProgress()) {
return (
<IconButton onClick={handleDeleteClick} edge="end" size="small">
<IconButton onClick={handleDeleteClick} edge="end" size="small" onKeyDown={(e) => handleDeleteKeyDown(e)}>
<DeleteOutlineIcon color="error" />
</IconButton>
);
Expand All @@ -108,5 +127,5 @@ export function DeleteUndoButton(props: DeleteUndoButtonProps): JSX.Element {
</IconButton>
);
}
return <UndoButtonWithProgress progressValue={progress} onUndo={handleUndoClick} />;
return <UndoButtonWithProgress progressValue={progress} onUndo={handleUndoClick} handleKeyDown={handleUndoDeleteKeyDown} />;
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Dispatch, SetStateAction } from 'react';
import { useTheme } from '@mui/material/styles';
import { SingleLayer } from './single-layer';
import { getSxClasses } from './left-panel-styles';
import { Box } from '@/ui';
import { useMapStoreActions } from '@/core/stores/';
import { useGeoViewMapId, useMapStoreActions } from '@/core/stores';
import { logger } from '@/core/utils/logger';
import { TypeLegendLayer } from '@/core/components/layers/types';
import { TABS } from '@/core/utils/constant';

interface LayerListProps {
depth: number;
layersList: TypeLegendLayer[];
setIsLayersListPanelVisible: Dispatch<SetStateAction<boolean>>;
showLayerDetailsPanel: (layer: TypeLegendLayer) => void;
isLayoutEnlarged: boolean;
}

export function LayersList({ layersList, setIsLayersListPanelVisible, isLayoutEnlarged, depth }: LayerListProps): JSX.Element {
export function LayersList({ layersList, showLayerDetailsPanel, isLayoutEnlarged, depth }: LayerListProps): JSX.Element {
// Log
logger.logTraceRender('components/layers/left-panel/layers-list');

const theme = useTheme();
const sxClasses = getSxClasses(theme);

const mapId = useGeoViewMapId();
const { getIndexFromOrderedLayerInfo } = useMapStoreActions();

const sortedLayers = layersList.sort((a, b) =>
Expand Down Expand Up @@ -49,12 +50,13 @@ export function LayersList({ layersList, setIsLayersListPanelVisible, isLayoutEn
const legendItems = sortedLayers.map((details, index) => {
const isFirst = index === 0;
const isLast = index === sortedLayers.length - 1;

return (
<SingleLayer
key={textToSlug(`layerKey-${index}-${details.layerPath}`)}
depth={depth}
layer={details}
setIsLayersListPanelVisible={setIsLayersListPanelVisible}
layer={{ ...details, layerId: `${mapId}-${TABS.LAYERS}-${details.layerPath}` }}
showLayerDetailsPanel={showLayerDetailsPanel}
index={index}
isFirst={isFirst}
isLast={isLast}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import {
useLayerDisplayState,
useLayerLegendLayers,
Expand All @@ -12,11 +12,11 @@ import { logger } from '@/core/utils/logger';
import { TypeLegendLayer } from '@/core/components/layers/types';

interface LeftPanelProps {
setIsLayersListPanelVisible: Dispatch<SetStateAction<boolean>>;
showLayerDetailsPanel: (layer: TypeLegendLayer) => void;
isLayoutEnlarged: boolean;
}

export function LeftPanel({ setIsLayersListPanelVisible, isLayoutEnlarged }: LeftPanelProps): JSX.Element {
export function LeftPanel({ showLayerDetailsPanel, isLayoutEnlarged }: LeftPanelProps): JSX.Element {
// Log
logger.logTraceRender('components/layers/left-panel/left-panel');

Expand Down Expand Up @@ -51,11 +51,12 @@ export function LeftPanel({ setIsLayersListPanelVisible, isLayoutEnlarged }: Lef
if (displayState === 'add') {
return <AddNewLayer />;
}

return (
<LayersList
layersList={orderedLegendLayers}
depth={0}
setIsLayersListPanelVisible={setIsLayersListPanelVisible}
showLayerDetailsPanel={showLayerDetailsPanel}
isLayoutEnlarged={isLayoutEnlarged}
/>
);
Expand Down
Loading

0 comments on commit afe5f9a

Please sign in to comment.