Skip to content

Instantly share code, notes, and snippets.

@kgonzago
Last active February 23, 2026 16:45
Show Gist options
  • Select an option

  • Save kgonzago/035d502c0e4330a120c417a5d8c9eae0 to your computer and use it in GitHub Desktop.

Select an option

Save kgonzago/035d502c0e4330a120c417a5d8c9eae0 to your computer and use it in GitHub Desktop.
filter + apply + map + details
/**
CONCEPT SNIPPET:
Demonstrates accessible UI patterns (status announcements, focus management)
using Calcite, Jimu UI, and ArcGIS Map components. Filtering logic and feature details
are intentionally mocked for illustration only.
**/
import React, { useEffect, useRef, useState } from "react";
// Jimu UI (Experience Builder)
import { Button } from "jimu-ui";
// Calcite Components React wrappers
import {
CalcitePanel,
CalciteLabel,
CalciteSelect,
CalciteOption
} from "@esri/calcite-components-react";
// ArcGIS Maps SDK for JavaScript components
import "@arcgis/map-components/components/arcgis-map";
import "@arcgis/map-components/components/arcgis-search";
import "@arcgis/map-components/components/arcgis-zoom";
import "@arcgis/map-components/components/arcgis-legend";
import "@arcgis/map-components/components/arcgis-feature";
type LayerKey = "parcels" | "buildings" | "roads";
export function AccessibleFilterMapWidget() {
const mapElRef = useRef<HTMLArcgisMapElement | null>(null);
// Ref for restoring focus after UI updates (pattern demo).
// Jimu UI Button may not always forward a raw HTMLButtonElement ref,
// so we keep this loose + add an id fallback.
const applyBtnRef = useRef<HTMLElement | null>(null);
const [layer, setLayer] = useState<LayerKey>("parcels");
const [status, setStatus] = useState("Loading map…");
const [selectedObjectId, setSelectedObjectId] = useState<number | null>(null);
// Example filter criteria (conceptual). In real code you’d apply this to a layer query/effect.
const whereByLayer: Record<LayerKey, string> = {
parcels: "1=1",
buildings: "1=1",
roads: "1=1"
};
useEffect(() => {
const mapEl = mapElRef.current;
if (!mapEl) return;
(async () => {
try {
await mapEl.viewOnReady();
const { portalItem } = mapEl.map;
mapEl.aria = {
label: portalItem.title,
};
setStatus("Map loaded.");
} catch {
setStatus("Map failed to load.");
}
})();
}, []);
const restoreApplyFocus = () => {
// Prefer ref if it works; fallback to id-based focus (robust for concept demo).
if (applyBtnRef.current && "focus" in applyBtnRef.current) {
(applyBtnRef.current as HTMLElement).focus();
return;
}
document.getElementById("apply-filters-btn")?.focus();
};
const applyFilter = async () => {
const whereQuery = whereByLayer[layer];
// Status text should be concise; it will be announced via live region.
setStatus(`Applying “${layer}” filter…`);
// Concept snippet: simulate async work.
await new Promise((r) => setTimeout(r, 250));
// Concept snippet: simulate a selected feature after filtering.
setSelectedObjectId(101);
setStatus(`Filter applied to “${layer}” (${whereQuery}). Showing selected feature details.`);
// Keep focus stable after updates (a11y pattern).
restoreApplyFocus();
};
return (
<div>
{/* Live region for status updates so screen readers hear changes. */}
<p
id="widget-status"
role="status"
aria-live="polite"
aria-atomic="true"
style={{ marginTop: 0 }}
>
{status}
{/* Optional helper for SR users; keep it short to avoid over-announcing. */}
<span style={{ position: "absolute", left: -10000, top: "auto", width: 1, height: 1, overflow: "hidden" }}>
Status updates will be announced.
</span>
</p>
<CalcitePanel heading="Layer filters" description="Select a layer and apply filters">
<CalciteLabel>
Select layer
<CalciteSelect
value={layer}
onCalciteSelectChange={(event) => {
const target = event.target as HTMLSelectElement;
setLayer(target.value as LayerKey);
setStatus(`Layer set to “${target.value}”. Ready to apply filter.`);
}}
>
<CalciteOption value="parcels">Parcels</CalciteOption>
<CalciteOption value="buildings">Buildings</CalciteOption>
<CalciteOption value="roads">Roads</CalciteOption>
</CalciteSelect>
</CalciteLabel>
{/* Jimu UI button: semantic button + keyboard support from the library */}
<Button
id="apply-filters-btn"
type="primary"
onClick={applyFilter}
// Use callback ref to attempt to capture focusable element
ref={(el: any) => {
// store any focusable element; the fallback by id covers the rest
applyBtnRef.current = el as unknown as HTMLElement;
}}
aria-describedby="widget-status"
>
Apply filters
</Button>
</CalcitePanel>
{/* Map components. Controls use slots inside arcgis-map. */}
<arcgis-map
ref={(el: HTMLArcgisMapElement) => (mapElRef.current = el)}
item-id="05e015c5f0314db9a487a9b46cb37eca"
>
<arcgis-search position="top-right"></arcgis-search>
<arcgis-zoom position="top-right"></arcgis-zoom>
<arcgis-legend position="bottom-left"></arcgis-legend>
</arcgis-map>
{/* Feature details outside the map for screen reader and keyboard users. */}
<section aria-label="Selected feature details">
{selectedObjectId == null ? (
<p>No feature selected.</p>
) : (
<arcgis-feature object-id={String(selectedObjectId)}></arcgis-feature>
)}
</section>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment