Skip to content

Commit f83b458

Browse files
author
Sean Nugent
committed
Accessibility overhaul
1 parent ed5eb15 commit f83b458

31 files changed

+2427
-478
lines changed

src/components/ActionButtons.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const ActionButtons = ({ resourceId, resourceUrl, resourceFormat, onApiClick })
1414
) : (
1515
<div className={styles.apiButtonPlaceholder} />
1616
)}
17-
<div style={{ flexGrow: 1 }} /> {/* Push download button to the right */}
1817
<DownloadButton
1918
resourceId={resourceId}
2019
resourceUrl={resourceUrl}

src/components/ApiButton.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import React from 'react';
2-
import styles from '../styles/Design_Style.module.css';
2+
import '@scottish-government/design-system/dist/css/design-system.min.css';
33

44
const ApiButton = ({ onClick }) => {
55
return (
6-
<button className={styles.apiButton} onClick={onClick}>
7-
API
6+
<button
7+
className="ds_button ds_button--secondary"
8+
style={{ marginBottom: 0 }}
9+
onClick={onClick}
10+
type="button"
11+
> API
812
</button>
913
);
1014
};
1115

12-
export default ApiButton;
16+
export default ApiButton;

src/components/AxisSelector.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22
import { Button, Menu, MenuItem } from '@mui/material';
33
import { ArrowDropDown as DropDownIcon } from '@mui/icons-material';
4+
import styles from '../styles/Embedded_Modal.module.css';
45

56
const AxisSelector = ({ axis, setAxis, columns, label, disabledAxis, includeCount }) => {
67
const [anchorEl, setAnchorEl] = useState(null);
@@ -11,6 +12,7 @@ const AxisSelector = ({ axis, setAxis, columns, label, disabledAxis, includeCoun
1112
variant="outlined"
1213
onClick={(e) => setAnchorEl(e.currentTarget)}
1314
endIcon={<DropDownIcon />}
15+
className={styles.analysisButton}
1416
>
1517
{label}: {axis}
1618
</Button>
@@ -27,6 +29,7 @@ const AxisSelector = ({ axis, setAxis, columns, label, disabledAxis, includeCoun
2729
setAnchorEl(null);
2830
}}
2931
disabled={column === disabledAxis}
32+
className={styles.chartTypeMenuItem}
3033
>
3134
{column}
3235
</MenuItem>
@@ -37,6 +40,7 @@ const AxisSelector = ({ axis, setAxis, columns, label, disabledAxis, includeCoun
3740
setAxis('count');
3841
setAnchorEl(null);
3942
}}
43+
className={styles.chartTypeMenuItem}
4044
disabled={'count' === disabledAxis}
4145
>
4246
Count

src/components/ChartTypeSelector.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22
import { Button, Menu, MenuItem } from '@mui/material';
33
import { ArrowDropDown as DropDownIcon } from '@mui/icons-material';
4+
import styles from '../styles/Embedded_Modal.module.css';
45

56
const ChartTypeSelector = ({ chartType, setChartType, chartTypes }) => {
67
const [anchorEl, setAnchorEl] = useState(null);
@@ -12,6 +13,7 @@ const ChartTypeSelector = ({ chartType, setChartType, chartTypes }) => {
1213
onClick={(e) => setAnchorEl(e.currentTarget)}
1314
startIcon={chartTypes.find(c => c.type === chartType)?.icon}
1415
endIcon={<DropDownIcon />}
16+
className={styles.analysisButton}
1517
>
1618
{chartType.charAt(0).toUpperCase() + chartType.slice(1)}
1719
</Button>
@@ -27,6 +29,7 @@ const ChartTypeSelector = ({ chartType, setChartType, chartTypes }) => {
2729
setChartType(type);
2830
setAnchorEl(null);
2931
}}
32+
className={styles.chartTypeMenuItem}
3033
>
3134
{icon} {type.charAt(0).toUpperCase() + type.slice(1)}
3235
</MenuItem>

src/components/DataDictionary.js

Lines changed: 87 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useEffect, useState, useRef } from 'react';
22
import PropTypes from 'prop-types';
33
import styles from '../styles/Design_Style.module.css';
44

@@ -8,6 +8,7 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
88
const [isDictionaryOpen, setIsDictionaryOpen] = useState(false);
99
const [geoJsonStructure, setGeoJsonStructure] = useState(null);
1010
const [expandedRows, setExpandedRows] = useState({});
11+
const accordionLabelRef = useRef(null);
1112

1213
useEffect(() => {
1314
if (!dataset || dataDictionary.length > 0) return;
@@ -87,7 +88,7 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
8788
};
8889

8990
const parseJsonStructure = (jsonData) => {
90-
const parseObject = (obj) => {
91+
const parseObject = (obj, level = 0) => {
9192
return Object.keys(obj).map((key) => {
9293
const value = obj[key];
9394
const isArray = Array.isArray(value);
@@ -97,7 +98,8 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
9798
name: key,
9899
type: isArray ? 'Array' : isObject ? 'Object' : typeof value,
99100
description: isArray ? 'Array' : isObject ? 'Object' : 'Primitive',
100-
children: isObject ? parseObject(value) : null,
101+
children: isObject ? parseObject(value, level + 1) : null,
102+
level,
101103
};
102104
});
103105
};
@@ -109,6 +111,7 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
109111
type: 'Array',
110112
description: 'Array',
111113
children: jsonData.length > 0 ? parseObject(jsonData[0]) : null,
114+
level: 0,
112115
},
113116
];
114117
} else if (typeof jsonData === 'object' && jsonData !== null) {
@@ -125,15 +128,48 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
125128
setIsDictionaryOpen(!isDictionaryOpen);
126129
};
127130

131+
const handleKeyDown = (event) => {
132+
if (event.key === 'Enter') {
133+
handleToggleDictionary();
134+
}
135+
};
136+
128137
const toggleRow = (index) => {
129138
setExpandedRows((prev) => ({
130139
...prev,
131140
[index]: !prev[index],
132141
}));
133142
};
134143

144+
const renderRows = (items) => {
145+
return items.map((item, index) => (
146+
<React.Fragment key={index}>
147+
<tr
148+
className={`${index % 2 === 0 ? styles.evenRow : styles.oddRow} ${
149+
expandedRows[index] ? styles.expandedRow : ''
150+
}`}
151+
onClick={() => toggleRow(index)}
152+
>
153+
<td style={{ wordBreak: 'break-word', paddingLeft: `${item.level * 20}px` }}>
154+
<span className={styles.fieldName}>
155+
{item.name} {item.children && <span className={styles.toggleIcon}></span>}
156+
</span>
157+
</td>
158+
<td style={{ wordBreak: 'break-word' }}>
159+
<span className={styles.typeBadge}>{item.type}</span>
160+
</td>
161+
<td style={{ wordBreak: 'break-word' }}>{item.description}</td>
162+
</tr>
163+
{expandedRows[index] && item.children && renderRows(item.children)}
164+
</React.Fragment>
165+
));
166+
};
167+
135168
return (
136-
<div className="ds_accordion" style={{ width: '100%', marginTop: '2rem' }}>
169+
<div
170+
className="ds_accordion"
171+
style={{ width: '100%', maxWidth: '100%', marginTop: '2rem', boxSizing: 'border-box' }}
172+
>
137173
<div className="ds_accordion-item">
138174
<input
139175
type="checkbox"
@@ -153,81 +189,77 @@ const DataDictionary = ({ dataset, resourceId, config }) => {
153189
)}
154190
</h3>
155191
<span className={styles.accordionIndicator}></span>
156-
<label className="ds_accordion-item__label" htmlFor="data-dictionary-accordion">
157-
<span className="visually-hidden">Show this section</span>
192+
<label
193+
className="ds_accordion-item__label"
194+
htmlFor="data-dictionary-accordion"
195+
ref={accordionLabelRef}
196+
tabIndex={0}
197+
onKeyDown={handleKeyDown}
198+
role="button"
199+
aria-expanded={isDictionaryOpen}
200+
aria-controls="data-dictionary-body"
201+
>
202+
<span className="visually-hidden">Toggle data dictionary</span>
158203
</label>
159204
</div>
160-
<div className="ds_accordion-item__body">
161-
<div className={styles.tableWrapper}>
205+
<div className="ds_accordion-item__body" id="data-dictionary-body">
206+
<div
207+
className={styles.tableWrapper}
208+
style={{ width: '100%', maxWidth: '100%', overflowX: 'auto', boxSizing: 'border-box' }}
209+
>
162210
{geoJsonStructure ? (
163211
<div>
164212
<h4>JSON Structure</h4>
165-
<table className={styles.tableModern}>
213+
<table
214+
className={`${styles.tableModern} ${styles.smallFont}`}
215+
style={{
216+
width: '100%',
217+
maxWidth: '100%',
218+
borderRadius: 0,
219+
tableLayout: 'auto',
220+
boxSizing: 'border-box',
221+
}}
222+
>
166223
<thead>
167224
<tr>
168-
<th>Field Name</th>
169-
<th>Type</th>
170-
<th>Description</th>
225+
<th style={{ wordBreak: 'break-word' }}>Field Name</th>
226+
<th style={{ wordBreak: 'break-word' }}>Type</th>
227+
<th style={{ wordBreak: 'break-word' }}>Description</th>
171228
</tr>
172229
</thead>
173-
<tbody>
174-
{geoJsonStructure.map((item, index) => (
175-
<React.Fragment key={index}>
176-
<tr
177-
className={`${index % 2 === 0 ? styles.evenRow : styles.oddRow} ${
178-
expandedRows[index] ? styles.expandedRow : ''
179-
}`}
180-
onClick={() => toggleRow(index)}
181-
>
182-
<td>
183-
<span className={styles.fieldName}>
184-
{item.name} {item.children && <span className={styles.toggleIcon}></span>}
185-
</span>
186-
</td>
187-
<td>
188-
<span className={styles.typeBadge}>{item.type}</span>
189-
</td>
190-
<td>{item.description}</td>
191-
</tr>
192-
{expandedRows[index] &&
193-
item.children &&
194-
item.children.map((child, idx) => (
195-
<tr key={idx} className={index % 2 === 0 ? styles.evenRow : styles.oddRow}>
196-
<td style={{ paddingLeft: '20px' }}>
197-
<span className={styles.fieldName}>{child.name}</span>
198-
</td>
199-
<td>
200-
<span className={styles.typeBadge}>{child.type}</span>
201-
</td>
202-
<td>{child.description}</td>
203-
</tr>
204-
))}
205-
</React.Fragment>
206-
))}
207-
</tbody>
230+
<tbody>{renderRows(geoJsonStructure)}</tbody>
208231
</table>
209232
</div>
210233
) : dataDictionary.length > 0 ? (
211-
<table className={styles.tableModern}>
234+
<table
235+
className={`${styles.tableModern} ${styles.smallFont}`}
236+
style={{
237+
width: '100%',
238+
maxWidth: '100%',
239+
borderRadius: 0,
240+
tableLayout: 'auto',
241+
boxSizing: 'border-box',
242+
}}
243+
>
212244
<thead>
213245
<tr>
214-
<th>Field Name</th>
215-
<th>Type</th>
216-
<th>Description</th>
246+
<th style={{ wordBreak: 'break-word' }}>Field Name</th>
247+
<th style={{ wordBreak: 'break-word' }}>Type</th>
248+
<th style={{ wordBreak: 'break-word' }}>Description</th>
217249
</tr>
218250
</thead>
219251
<tbody>
220252
{dataDictionary
221253
.filter((field) => field.name !== '_id')
222254
.map((field, index) => (
223255
<tr key={index} className={index % 2 === 0 ? styles.evenRow : styles.oddRow}>
224-
<td>
256+
<td style={{ wordBreak: 'break-word' }}>
225257
<span className={styles.fieldName}>{field.name}</span>
226258
</td>
227-
<td>
259+
<td style={{ wordBreak: 'break-word' }}>
228260
<span className={styles.typeBadge}>{field.type}</span>
229261
</td>
230-
<td>{field.description}</td>
262+
<td style={{ wordBreak: 'break-word' }}>{field.description}</td>
231263
</tr>
232264
))}
233265
</tbody>
@@ -248,4 +280,4 @@ DataDictionary.propTypes = {
248280
config: PropTypes.object.isRequired,
249281
};
250282

251-
export default DataDictionary;
283+
export default DataDictionary;

src/components/DownloadButton.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState, useRef } from 'react';
2-
import styles from '../styles/Design_Style.module.css';
2+
import '@scottish-government/design-system/dist/css/design-system.min.css';
33
import config from '../config.js';
4+
import styles from '../styles/Design_Style.module.css';
45

56
const DownloadButton = ({ resourceId, resourceUrl, resourceFormat }) => {
67
const [isOpen, setIsOpen] = useState(false);
@@ -12,28 +13,26 @@ const DownloadButton = ({ resourceId, resourceUrl, resourceFormat }) => {
1213

1314
// Handle download
1415
const handleDownload = (format) => {
15-
const url = format === 'native'
16-
? resourceUrl
17-
: `${config.apiBaseUrl}/datastore/dump/${resourceId}?format=${format}`;
16+
const url = format === 'native' ? resourceUrl : `${config.apiBaseUrl}/datastore/dump/${resourceId}?format=${format}`;
1817
window.location.href = url;
18+
setIsOpen(false);
1919
};
2020

2121
return (
2222
<div className={styles.downloadWrapper} ref={wrapperRef}>
2323
<button
24-
className={`${styles.primaryButton} ${isGeoJSON ? '' : styles.hasDropdown}`}
24+
className={`ds_button ${!isGeoJSON ? styles.hasDropdown : ''}`}
2525
onClick={toggleDropdown}
2626
>
27-
<span className={styles.buttonText}>Download</span>
28-
{!isGeoJSON && <span className={styles.dropdownChevron}></span>}
27+
Download{!isGeoJSON && ' ▾'}
2928
</button>
3029
{isOpen && !isGeoJSON && (
31-
<div className={styles.dropdownMenu}>
32-
<button onClick={() => handleDownload('csv')}>CSV</button>
33-
<button onClick={() => handleDownload('json')}>JSON</button>
34-
<button onClick={() => handleDownload('xml')}>XML</button>
35-
<button onClick={() => handleDownload('tsv')}>TSV</button>
36-
</div>
30+
<div className={styles.dropdownMenu}>
31+
<button onClick={() => handleDownload('csv')}>CSV</button>
32+
<button onClick={() => handleDownload('json')}>JSON</button>
33+
<button onClick={() => handleDownload('xml')}>XML</button>
34+
<button onClick={() => handleDownload('tsv')}>TSV</button>
35+
</div>
3736
)}
3837
</div>
3938
);

0 commit comments

Comments
 (0)