-
Notifications
You must be signed in to change notification settings - Fork 27
Feature : [DEV-50357] - Add Grouped Legend to the Maps #2076
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
Changes from 11 commits
5f96283
a1a0e5e
0ab2314
92f64e5
b857c3f
6fa8038
23d6dff
9d5b85d
3f5e5d0
54d4e06
e7f509f
878e15a
4bc6f48
2fc71f4
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 |
---|---|---|
|
@@ -15,4 +15,5 @@ export type Legend = { | |
singleRow: boolean | ||
type: string | ||
verticalSorted: boolean | ||
groupBy: string | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
.group-label { | ||
font-weight: 500; | ||
font-family: Nunito, sans-serif; | ||
font-size: 1rem; | ||
margin-bottom: 0.5rem; | ||
} | ||
.group-list-item { | ||
list-style: none; | ||
font-weight: 400; | ||
font-size: 0.889rem; | ||
margin-top: 0.5rem !important; | ||
cursor: pointer; | ||
} | ||
|
||
.group-container { | ||
margin-bottom: 2rem; | ||
} | ||
|
||
.legend-group-item-disable { | ||
opacity: 0.4; | ||
} | ||
|
||
.legend-group-item-not-disable { | ||
outline: 1px solid #005ea2; | ||
outline-offset: 5px; | ||
border-radius: 1px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { useContext, useMemo } from 'react' | ||
import './Legend.Group.css' | ||
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. [fix]: this will need to be updated to the new name. |
||
import LegendShape from '@cdc/core/components/LegendShape' | ||
import { toggleLegendActive } from '@cdc/map/src/helpers/toggleLegendActive' | ||
import ErrorBoundary from '@cdc/core/components/ErrorBoundary' | ||
import ConfigContext from '../../../../context' | ||
|
||
interface LegendItem { | ||
color: string | ||
label: string | ||
disabled?: boolean | ||
special: boolean | ||
} | ||
|
||
interface GroupedData { | ||
[key: string]: LegendItem[] | ||
} | ||
|
||
const LegendGroup = ({ legendItems }) => { | ||
const { runtimeLegend, setAccessibleStatus, setRuntimeLegend, state, currentViewport } = useContext(ConfigContext) | ||
|
||
const getGridColClass = (viewport: string) => { | ||
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. Instead of using the viewport can you use bootstraps helpers? col-number, col-md-number, col-lg-number? |
||
const map = { xs: 'col-12', sm: 'col-6', md: 'col-4' } | ||
return map[viewport] || 'col-3' | ||
} | ||
|
||
const groupLegendItems = (items: LegendItem[], data: object[], groupByKey: string): GroupedData => { | ||
if (!groupByKey || !data || !items) return {} | ||
|
||
const columnKey = state.columns.primary.name || '' | ||
const result: GroupedData = {} | ||
|
||
for (const row of data) { | ||
const groupValue = row[groupByKey] | ||
if (!groupValue) continue | ||
|
||
const label = row[columnKey] | ||
const match = items.find(i => i.label === label) | ||
if (!match) continue | ||
|
||
result[groupValue] ||= [] | ||
if (!result[groupValue].some(i => i.label === label)) { | ||
result[groupValue].push(match) | ||
} | ||
} | ||
|
||
// Sort items in each group | ||
Object.entries(result).forEach(([group, items]) => { | ||
result[group] = [...items].sort( | ||
(a, b) => | ||
state.legend.categoryValuesOrder?.indexOf(a.label) - state.legend.categoryValuesOrder?.indexOf(b.label) | ||
) | ||
Atash3000 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}) | ||
|
||
return result | ||
} | ||
|
||
const handleToggleItem = (item: LegendItem) => { | ||
const newItems = runtimeLegend.items.map(legend => | ||
legend.value === item.label ? { ...legend, disabled: !legend.disabled } : legend | ||
) | ||
|
||
const wasDisabled = runtimeLegend.items.find(i => i.value === item.label)?.disabled | ||
const delta = wasDisabled ? -1 : 1 | ||
|
||
setRuntimeLegend({ | ||
...runtimeLegend, | ||
items: newItems, | ||
disabledAmt: (runtimeLegend.disabledAmt ?? 0) + delta | ||
}) | ||
|
||
setAccessibleStatus( | ||
`${wasDisabled ? 'Enabled' : 'Disabled'} legend item ${item.label}. Please reference the data table.` | ||
) | ||
} | ||
|
||
const getLegendItemClasses = (item: LegendItem, hasDisabledItems: boolean) => { | ||
return [ | ||
'group-list-item', | ||
item.disabled ? 'legend-group-item-disable' : hasDisabledItems ? 'legend-group-item-not-disable' : '' | ||
] | ||
.filter(Boolean) | ||
.join(' ') | ||
} | ||
|
||
const gridClass = | ||
state.legend.position === 'bottom' || state.legend.position === 'top' ? getGridColClass(currentViewport) : 'col-12' | ||
|
||
const groupedData = useMemo( | ||
() => groupLegendItems(legendItems, state.data, state.legend.groupBy), | ||
[legendItems, state.data, state.legend.groupBy] | ||
) | ||
|
||
const hasDisabledItems = runtimeLegend.items.some(item => item.disabled) | ||
|
||
return ( | ||
<ErrorBoundary component='Grouped Legend'> | ||
<div className='row'> | ||
{Object.entries(groupedData).map(([groupName, items]) => ( | ||
<div className={`${gridClass} group-container`} key={groupName}> | ||
<p className='group-label'>{groupName}</p> | ||
<ul className='row'> | ||
{items.map((item, index) => ( | ||
<li | ||
key={`${item.label}-${index}`} | ||
role='button' | ||
tabIndex={0} | ||
title={`Legend item ${item.label} - Click to disable`} | ||
className={getLegendItemClasses(item, hasDisabledItems)} | ||
onClick={() => handleToggleItem(item)} | ||
onKeyDown={e => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
e.preventDefault() | ||
toggleLegendActive(index, item.label, runtimeLegend, setRuntimeLegend, setAccessibleStatus) | ||
} | ||
}} | ||
> | ||
<LegendShape shape={state.legend.style === 'boxes' ? 'square' : 'circle'} fill={item.color} /> | ||
<span>{item.label}</span> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
))} | ||
</div> | ||
</ErrorBoundary> | ||
) | ||
} | ||
|
||
export default LegendGroup |
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.
[fix]: rename and fix import to the lowercase version based on new convention of having styles start lowercased.
legend.group.css