Skip to content

Commit ce32fbd

Browse files
committed
webui: enhance ESLint config, improve JSX accessibility, and optimize Vite build settings
1 parent 0f43252 commit ce32fbd

File tree

24 files changed

+111
-68
lines changed

24 files changed

+111
-68
lines changed

webui/eslint.config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export default [
1919
sourceType: "module",
2020
globals: {
2121
...globals.browser,
22+
JSX: "readonly",
23+
Options: "readonly",
24+
process: "readonly",
25+
Buffer: "readonly",
26+
__buildVersion: "readonly",
2227
},
2328
parserOptions: {
2429
ecmaFeatures: { jsx: true },
@@ -78,6 +83,11 @@ export default [
7883
"react-hooks/exhaustive-deps": "warn",
7984
"react/prop-types": "off",
8085
"react/jsx-key": "error",
86+
"jsx-a11y/no-autofocus": "error",
87+
"jsx-a11y/click-events-have-key-events": "error",
88+
"jsx-a11y/no-static-element-interactions": "error",
89+
"jsx-a11y/anchor-is-valid": "error",
90+
"jsx-a11y/alt-text": "error",
8191
},
8292
},
8393
{

webui/src/lib/components/auth/credentials.jsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ export const CredentialsTable = ({userId, currentAccessKey, refresh, after, onPa
5151
results={results}
5252
headers={['Access Key ID', 'Creation Date', '']}
5353
rowFn={row => [
54-
<>
54+
<span key={`access-key-${row.access_key_id}`}>
5555
<code>{row.access_key_id}</code>
5656
{(currentAccessKey === row.access_key_id) && <strong>{' '}(current)</strong>}
57-
</>,
58-
<FormattedDate dateValue={row.creation_date}/>,
59-
<span className="row-hover">
57+
</span>,
58+
<FormattedDate key={`date-${row.access_key_id}`} dateValue={row.creation_date}/>,
59+
<span key={`actions-${row.access_key_id}`} className="row-hover">
6060
{(currentAccessKey !== row.access_key_id) &&
6161
<ConfirmationButton
6262
variant="outline-danger"
@@ -81,11 +81,11 @@ export const CredentialsTable = ({userId, currentAccessKey, refresh, after, onPa
8181

8282

8383
export const CredentialsShowModal = ({credentials, show, onHide}) => {
84-
if (!credentials) return <></>;
85-
8684
const {state} = useContext(AppContext);
8785
const buttonVariant = state.settings.darkMode ? "outline-light" : "outline-dark";
8886

87+
if (!credentials) return <></>;;
88+
8989
return (
9090
<Modal show={show} onHide={onHide} size="lg">
9191
<Modal.Header closeButton>

webui/src/lib/components/auth/forms.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ export const AttachModal = ({
4343
results={response.results}
4444
rowFn={ent => [
4545
<Checkbox
46+
key={`checkbox-${ent.id}`}
4647
defaultChecked={selected.some(selectedEnt => selectedEnt.id === ent.id)}
4748
onAdd={() => setSelected([...selected, ent])}
4849
onRemove={() => setSelected(selected.filter(selectedEnt => selectedEnt.id !== ent.id))}
4950
name={'selected'}/>,
50-
<strong>{resolveEntityFn(ent)}</strong>
51+
<strong key={`entity-${ent.id}`}>{resolveEntityFn(ent)}</strong>
5152
]}
5253
firstFixedCol={true}
5354
/>
@@ -186,7 +187,7 @@ export const EntityActionModal = ({
186187
e.preventDefault()
187188
onSubmit()
188189
}}>
189-
<FormControl ref={idField} autoFocus placeholder={placeholder} type="text"/>
190+
<FormControl ref={idField} placeholder={placeholder} type="text"/>
190191
{showExtraField &&
191192
<FormControl ref={extraField} placeholder={extraPlaceholder} type="text" className="mt-3"/>
192193
}

webui/src/lib/components/controls.jsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ export const PrefixSearchWidget = ({ onFilter, text = "Search by Prefix", defaul
324324
<InputGroup className="prefix-search-input-group">
325325
<Form.Control
326326
ref={ref}
327-
autoFocus
328327
defaultValue={defaultValue}
329328
placeholder={text}
330329
aria-label={text}
@@ -493,7 +492,7 @@ export const ExitConfirmationDialog = ({dialogAlert, dialogDescription, onExit,
493492
</DialogContentText>
494493
</DialogContent>
495494
<DialogActions>
496-
<MuiButton onClick={onContinue} autoFocus>Cancel</MuiButton>
495+
<MuiButton onClick={onContinue}>Cancel</MuiButton>
497496
<MuiButton onClick={onExit}>
498497
Exit
499498
</MuiButton>
@@ -535,7 +534,6 @@ export const SearchInput = ({searchPrefix, setSearchPrefix, placeholder}) => {
535534
return (
536535
<InputGroup>
537536
<Form.Control
538-
autoFocus
539537
placeholder={placeholder}
540538
value={searchPrefix}
541539
onChange={(e) => setSearchPrefix(e.target.value)}

webui/src/lib/components/policy.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const PolicyEditor = ({ show, onHide, onSubmit, policy = null, noID = fal
7777
}}>
7878
{(policy === null) && !noID && (
7979
<Form.Group className="mb-3">
80-
<FormControl ref={idField} autoFocus placeholder="Policy ID (e.g. 'MyRepoReadWrite')" type="text"/>
80+
<FormControl ref={idField} placeholder="Policy ID (e.g. 'MyRepoReadWrite')" type="text"/>
8181
</Form.Group>
8282
)}
8383
<Form.Group className="mb-3">

webui/src/lib/components/repository/ChangeSummary.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const ChangeSummary = ({prefix, getMore}) => {
8686
</Tooltip>
8787
}>
8888
<small>
89-
&gt;= {MAX_NUM_OBJECTS.toLocaleString()} results, <a href="#" onClick={onLoadAll}>load more?</a>
89+
&gt;= {MAX_NUM_OBJECTS.toLocaleString()} results, <button type="button" className="btn-link" onClick={onLoadAll}>load more?</button>
9090
</small>
9191
</OverlayTrigger>
9292
)

webui/src/lib/components/repository/ObjectsDiff.jsx

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,44 +27,46 @@ export const ObjectsDiff = ({diffType, repoId, leftRef, rightRef, path}) => {
2727
if (hooksError) return <AlertError error={hooksError}/>;
2828

2929
const readable = readableObject(path);
30-
let left;
31-
let right;
32-
switch (diffType) {
33-
case 'changed':
34-
case 'conflict':
35-
left = useAPI(async () => objects.getStat(repoId, leftRef, path),
36-
[repoId, leftRef, path]);
37-
right = useAPI(async () => objects.getStat(repoId, rightRef, path),
38-
[repoId, rightRef, path]);
39-
break;
40-
case 'added':
41-
right = useAPI(async () => objects.getStat(repoId, rightRef, path),
42-
[repoId, rightRef, path]);
43-
break;
44-
case 'removed':
45-
left = useAPI(async () => objects.getStat(repoId, leftRef, path),
46-
[repoId, leftRef, path]);
47-
break;
48-
default:
49-
return <AlertError error={"Unsupported diff type " + diffType}/>;
30+
// Always call hooks at the top level
31+
const leftStatResponse = useAPI(async () => {
32+
if (diffType === 'changed' || diffType === 'conflict' || diffType === 'removed') {
33+
return objects.getStat(repoId, leftRef, path);
34+
}
35+
return null;
36+
}, [repoId, leftRef, path, diffType]);
37+
38+
const rightStatResponse = useAPI(async () => {
39+
if (diffType === 'changed' || diffType === 'conflict' || diffType === 'added') {
40+
return objects.getStat(repoId, rightRef, path);
41+
}
42+
return null;
43+
}, [repoId, rightRef, path, diffType]);
44+
45+
if (!['changed', 'conflict', 'added', 'removed'].includes(diffType)) {
46+
return <AlertError error={"Unsupported diff type " + diffType}/>;
5047
}
5148

49+
let left = leftStatResponse;
50+
let right = rightStatResponse;
51+
52+
const hooksLoading = (left && left.loading) || (right && right.loading);
53+
5254
if (hooksLoading || (left && left.loading) || (right && right.loading)) return <Loading/>;
5355
const err = (left && left.error) || (right && right.err);
5456
if (err) return <AlertError error={err}/>;
5557

56-
const leftStat = left && left.response;
57-
const rightStat = right && right.response;
58+
const leftStatResponseResponse = left && left.response;
59+
const rightStatResponseResponse = right && right.response;
5860
if (!readable) {
59-
return <NoContentDiff left={leftStat} right={rightStat} diffType={diffType}/>;
61+
return <NoContentDiff left={leftStatResponseResponse} right={rightStatResponseResponse} diffType={diffType}/>;
6062
}
61-
const objectTooBig = (leftStat && leftStat.size_bytes > maxDiffSizeBytes) || (rightStat && rightStat.size_bytes > maxDiffSizeBytes);
63+
const objectTooBig = (leftStatResponse && leftStatResponse.size_bytes > maxDiffSizeBytes) || (rightStatResponse && rightStatResponse.size_bytes > maxDiffSizeBytes);
6264
if (objectTooBig) {
6365
return <AlertError error={path + " is too big (> " + humanSize(maxDiffSizeBytes)+ "). To view its diff please download" +
6466
" the objects and use an external diff tool."}/>
6567
}
66-
const leftSize = leftStat && leftStat.size_bytes;
67-
const rightSize = rightStat && rightStat.size_bytes;
68+
const leftSize = leftStatResponse && leftStatResponse.size_bytes;
69+
const rightSize = rightStatResponse && rightStatResponse.size_bytes;
6870
return <ContentDiff
6971
config={storageConfig}
7072
repoId={repoId}
@@ -122,10 +124,19 @@ const ContentDiff = ({config, repoId, path, leftRef, rightRef, leftSize, rightSi
122124

123125

124126
const TextDiff = ({ config, repoId, path, leftRef, rightRef, leftSize, rightSize, diffType, isDarkMode }) => {
125-
const left = leftRef && useAPI(async () => objects.get(repoId, leftRef, path, config.pre_sign_support_ui),
126-
[repoId, leftRef, path]);
127-
const right = rightRef && useAPI(async () => objects.get(repoId, rightRef, path, config.pre_sign_support_ui),
128-
[repoId, rightRef, path]);
127+
const left = useAPI(async () => {
128+
if (leftRef) {
129+
return objects.get(repoId, leftRef, path, config.pre_sign_support_ui);
130+
}
131+
return null;
132+
}, [repoId, leftRef, path, config.pre_sign_support_ui]);
133+
134+
const right = useAPI(async () => {
135+
if (rightRef) {
136+
return objects.get(repoId, rightRef, path, config.pre_sign_support_ui);
137+
}
138+
return null;
139+
}, [repoId, rightRef, path, config.pre_sign_support_ui]);
129140

130141
if ((left && left.loading) || (right && right.loading)) return <Loading/>;
131142
const err = (left && left.error) || (right && right.error);

webui/src/lib/components/repository/commits.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const CommitMetadataTable = ({ commit }) => {
5656
</thead>
5757
<tbody>
5858
{keys.map(key =>
59-
<MetadataRow metadata_key={key} metadata_value={commit.metadata[key]}/>)}
59+
<MetadataRow key={key} metadata_key={key} metadata_value={commit.metadata[key]}/>)}
6060
</tbody>
6161
</Table>
6262
</>
@@ -69,7 +69,7 @@ const CommitMetadataUIButtons = ({ commit }) => {
6969

7070
return (
7171
<>{
72-
keys.map((key) => <MetadataUIButton metadata_key={key} metadata_value={commit.metadata[key]}/>)
72+
keys.map((key) => <MetadataUIButton key={key} metadata_key={key} metadata_value={commit.metadata[key]}/>)
7373
}</>
7474
);
7575
};

webui/src/lib/components/repository/treeRows.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,14 @@ export const PrefixTreeEntryRow = ({entry, relativeTo = "", dirExpanded, depth =
9797
);
9898
};
9999
const PrefixExpansionSection = ({dirExpanded, onClick}) => {
100-
return (<span onClick={onClick} className="prefix-expand-icon">
100+
return (<button
101+
type="button"
102+
onClick={onClick}
103+
className="prefix-expand-icon btn btn-link p-0 border-0"
104+
aria-label={dirExpanded ? "Collapse directory" : "Expand directory"}
105+
>
101106
{dirExpanded ? <ChevronDownIcon/> : <ChevronRightIcon/>}
102-
</span>)
107+
</button>)
103108
}
104109

105110
const TableRow = ({diffIndicator, depth, loading, showSummary, entry, getMore, rowActions,

webui/src/pages/auth/groups/group/members.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ const GroupMemberList = ({ groupId, after, onPaginate }) => {
7373
<DataTable
7474
keyFn={user => user.id}
7575
rowFn={user => [
76-
<Link href={{pathname: '/auth/users/:userId', params: {userId: user.id}}}>{resolveUserDisplayName(user)}</Link>,
77-
<FormattedDate dateValue={user.creation_date}/>
76+
<Link key={`user-${user.id}`} href={{pathname: '/auth/users/:userId', params: {userId: user.id}}}>{resolveUserDisplayName(user)}</Link>,
77+
<FormattedDate key={`date-${user.id}`} dateValue={user.creation_date}/>
7878
]}
7979
headers={['User ID', 'Created At']}
8080
actions={[{

0 commit comments

Comments
 (0)