Skip to content

Commit a5f68a3

Browse files
committed
Merge branch 'dspace-8_x' into accessibility-settings-8_x
2 parents da5a4f3 + 8f7e9aa commit a5f68a3

305 files changed

Lines changed: 4140 additions & 1176 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@
293293
],
294294
"rules": {
295295
// Custom DSpace Angular rules
296-
"dspace-angular-html/themed-component-usages": "error"
296+
"dspace-angular-html/themed-component-usages": "error",
297+
"dspace-angular-html/no-disabled-attribute-on-button": "error"
297298
}
298299
},
299300
{

config/config.example.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,31 @@ ssr:
2323
# Determining which styles are critical is a relatively expensive operation; this option is
2424
# disabled (false) by default to boost server performance at the expense of loading smoothness.
2525
inlineCriticalCss: false
26+
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
27+
# NOTE: The "/handle/" path ensures Handle redirects work via SSR. The "/reload/" path ensures
28+
# hard refreshes (e.g. after login) trigger SSR while fully reloading the page.
29+
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/', '/reload/' ]
30+
# Whether to enable rendering of Search component on SSR.
31+
# If set to true the component will be included in the HTML returned from the server side rendering.
32+
# If set to false the component will not be included in the HTML returned from the server side rendering.
33+
enableSearchComponent: false
34+
# Whether to enable rendering of Browse component on SSR.
35+
# If set to true the component will be included in the HTML returned from the server side rendering.
36+
# If set to false the component will not be included in the HTML returned from the server side rendering.
37+
enableBrowseComponent: false
38+
# Enable state transfer from the server-side application to the client-side application.
39+
# Defaults to true.
40+
# Note: When using an external application cache layer, it's recommended not to transfer the state to avoid caching it.
41+
# Disabling it ensures that dynamic state information is not inadvertently cached, which can improve security and
42+
# ensure that users always use the most up-to-date state.
43+
transferState: true
44+
# When a different REST base URL is used for the server-side application, the generated state contains references to
45+
# REST resources with the internal URL configured. By default, these internal URLs are replaced with public URLs.
46+
# Disable this setting to avoid URL replacement during SSR. In this the state is not transferred to avoid security issues.
47+
replaceRestUrl: true
48+
# Enable request performance profiling data collection and printing the results in the server console.
49+
# Defaults to false. Enabling in production is NOT recommended
50+
#enablePerformanceProfiler: false
2651

2752
# The REST API server settings
2853
# NOTE: these settings define which (publicly available) REST API to use. They are usually
@@ -33,6 +58,9 @@ rest:
3358
port: 443
3459
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
3560
nameSpace: /server
61+
# Provide a different REST url to be used during SSR execution. It must contain the whole url including protocol, server port and
62+
# server namespace (uncomment to use it).
63+
#ssrBaseUrl: http://localhost:8080/server
3664

3765
# Caching settings
3866
cache:
@@ -448,6 +476,12 @@ search:
448476
enabled: false
449477
# List of filters to enable in "Advanced Search" dropdown
450478
filter: [ 'title', 'author', 'subject', 'entityType' ]
479+
#
480+
# Number used to render n UI elements called loading skeletons that act as placeholders.
481+
# These elements indicate that some content will be loaded in their stead.
482+
# Since we don't know how many filters will be loaded before we receive a response from the server we use this parameter for the skeletons count.
483+
# e.g. If we set 5 then 5 loading skeletons will be visualized before the actual filters are retrieved.
484+
defaultFiltersCount: 5
451485

452486

453487
# Notify metrics

docs/lint/html/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
_______
33

44
- [`dspace-angular-html/themed-component-usages`](./rules/themed-component-usages.md): Themeable components should be used via the selector of their `ThemedComponent` wrapper class
5+
- [`dspace-angular-html/no-disabled-attribute-on-button`](./rules/no-disabled-attribute-on-button.md): Buttons should use the `dsBtnDisabled` directive instead of the HTML `disabled` attribute.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
[DSpace ESLint plugins](../../../../lint/README.md) > [HTML rules](../index.md) > `dspace-angular-html/no-disabled-attribute-on-button`
2+
_______
3+
4+
Buttons should use the `dsBtnDisabled` directive instead of the HTML `disabled` attribute.
5+
This should be done to ensure that users with a screen reader are able to understand that the a button button is present, and that it is disabled.
6+
The native html disabled attribute does not allow users to navigate to the button by keyboard, and thus they have no way of knowing that the button is present.
7+
8+
_______
9+
10+
[Source code](../../../../lint/src/rules/html/no-disabled-attribute-on-button.ts)
11+
12+
### Examples
13+
14+
15+
#### Valid code
16+
17+
##### should use [dsBtnDisabled] in HTML templates
18+
19+
```html
20+
<button [dsBtnDisabled]="true">Submit</button>
21+
```
22+
23+
##### disabled attribute is still valid on non-button elements
24+
25+
```html
26+
<input disabled>
27+
```
28+
29+
##### [disabled] attribute is still valid on non-button elements
30+
31+
```html
32+
<input [disabled]="true">
33+
```
34+
35+
##### angular dynamic attributes that use disabled are still valid
36+
37+
```html
38+
<button [class.disabled]="isDisabled">Submit</button>
39+
```
40+
41+
42+
43+
44+
#### Invalid code &amp; automatic fixes
45+
46+
##### should not use disabled attribute in HTML templates
47+
48+
```html
49+
<button disabled>Submit</button>
50+
```
51+
Will produce the following error(s):
52+
```
53+
Buttons should use the `dsBtnDisabled` directive instead of the `disabled` attribute.
54+
```
55+
56+
Result of `yarn lint --fix`:
57+
```html
58+
<button [dsBtnDisabled]="true">Submit</button>
59+
```
60+
61+
62+
##### should not use [disabled] attribute in HTML templates
63+
64+
```html
65+
<button [disabled]="true">Submit</button>
66+
```
67+
Will produce the following error(s):
68+
```
69+
Buttons should use the `dsBtnDisabled` directive instead of the `disabled` attribute.
70+
```
71+
72+
Result of `yarn lint --fix`:
73+
```html
74+
<button [dsBtnDisabled]="true">Submit</button>
75+
```
76+
77+
78+

lint/src/rules/html/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import {
1010
bundle,
1111
RuleExports,
1212
} from '../../util/structure';
13+
import * as noDisabledAttributeOnButton from './no-disabled-attribute-on-button';
1314
import * as themedComponentUsages from './themed-component-usages';
1415

1516
const index = [
1617
themedComponentUsages,
18+
noDisabledAttributeOnButton,
19+
1720
] as unknown as RuleExports[];
1821

1922
export = {
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import {
2+
TmplAstBoundAttribute,
3+
TmplAstTextAttribute,
4+
} from '@angular-eslint/bundled-angular-compiler';
5+
import { TemplateParserServices } from '@angular-eslint/utils';
6+
import {
7+
ESLintUtils,
8+
TSESLint,
9+
} from '@typescript-eslint/utils';
10+
11+
import {
12+
DSpaceESLintRuleInfo,
13+
NamedTests,
14+
} from '../../util/structure';
15+
import { getSourceCode } from '../../util/typescript';
16+
17+
export enum Message {
18+
USE_DSBTN_DISABLED = 'mustUseDsBtnDisabled',
19+
}
20+
21+
export const info = {
22+
name: 'no-disabled-attribute-on-button',
23+
meta: {
24+
docs: {
25+
description: `Buttons should use the \`dsBtnDisabled\` directive instead of the HTML \`disabled\` attribute.
26+
This should be done to ensure that users with a screen reader are able to understand that the a button button is present, and that it is disabled.
27+
The native html disabled attribute does not allow users to navigate to the button by keyboard, and thus they have no way of knowing that the button is present.`,
28+
},
29+
type: 'problem',
30+
fixable: 'code',
31+
schema: [],
32+
messages: {
33+
[Message.USE_DSBTN_DISABLED]: 'Buttons should use the `dsBtnDisabled` directive instead of the `disabled` attribute.',
34+
},
35+
},
36+
defaultOptions: [],
37+
} as DSpaceESLintRuleInfo;
38+
39+
export const rule = ESLintUtils.RuleCreator.withoutDocs({
40+
...info,
41+
create(context: TSESLint.RuleContext<Message, unknown[]>) {
42+
const parserServices = getSourceCode(context).parserServices as TemplateParserServices;
43+
44+
/**
45+
* Some dynamic angular inputs will have disabled as name because of how Angular handles this internally (e.g [class.disabled]="isDisabled")
46+
* But these aren't actually the disabled attribute we're looking for, we can determine this by checking the details of the keySpan
47+
*/
48+
function isOtherAttributeDisabled(node: TmplAstBoundAttribute | TmplAstTextAttribute): boolean {
49+
// if the details are not null, and the details are not 'disabled', then it's not the disabled attribute we're looking for
50+
return node.keySpan?.details !== null && node.keySpan?.details !== 'disabled';
51+
}
52+
53+
/**
54+
* Replace the disabled text with [dsBtnDisabled] in the template
55+
*/
56+
function replaceDisabledText(text: string ): string {
57+
const hasBrackets = text.includes('[') && text.includes(']');
58+
const newDisabledText = hasBrackets ? 'dsBtnDisabled' : '[dsBtnDisabled]="true"';
59+
return text.replace('disabled', newDisabledText);
60+
}
61+
62+
function inputIsChildOfButton(node: any): boolean {
63+
return (node.parent?.tagName === 'button' || node.parent?.name === 'button');
64+
}
65+
66+
function reportAndFix(node: TmplAstBoundAttribute | TmplAstTextAttribute) {
67+
if (!inputIsChildOfButton(node) || isOtherAttributeDisabled(node)) {
68+
return;
69+
}
70+
71+
const sourceSpan = node.sourceSpan;
72+
context.report({
73+
messageId: Message.USE_DSBTN_DISABLED,
74+
loc: parserServices.convertNodeSourceSpanToLoc(sourceSpan),
75+
fix(fixer) {
76+
const templateText = sourceSpan.start.file.content;
77+
const disabledText = templateText.slice(sourceSpan.start.offset, sourceSpan.end.offset);
78+
const newText = replaceDisabledText(disabledText);
79+
return fixer.replaceTextRange([sourceSpan.start.offset, sourceSpan.end.offset], newText);
80+
},
81+
});
82+
}
83+
84+
return {
85+
'BoundAttribute[name="disabled"]'(node: TmplAstBoundAttribute) {
86+
reportAndFix(node);
87+
},
88+
'TextAttribute[name="disabled"]'(node: TmplAstTextAttribute) {
89+
reportAndFix(node);
90+
},
91+
};
92+
},
93+
});
94+
95+
export const tests = {
96+
plugin: info.name,
97+
valid: [
98+
{
99+
name: 'should use [dsBtnDisabled] in HTML templates',
100+
code: `
101+
<button [dsBtnDisabled]="true">Submit</button>
102+
`,
103+
},
104+
{
105+
name: 'disabled attribute is still valid on non-button elements',
106+
code: `
107+
<input disabled>
108+
`,
109+
},
110+
{
111+
name: '[disabled] attribute is still valid on non-button elements',
112+
code: `
113+
<input [disabled]="true">
114+
`,
115+
},
116+
{
117+
name: 'angular dynamic attributes that use disabled are still valid',
118+
code: `
119+
<button [class.disabled]="isDisabled">Submit</button>
120+
`,
121+
},
122+
],
123+
invalid: [
124+
{
125+
name: 'should not use disabled attribute in HTML templates',
126+
code: `
127+
<button disabled>Submit</button>
128+
`,
129+
errors: [{ messageId: Message.USE_DSBTN_DISABLED }],
130+
output: `
131+
<button [dsBtnDisabled]="true">Submit</button>
132+
`,
133+
},
134+
{
135+
name: 'should not use [disabled] attribute in HTML templates',
136+
code: `
137+
<button [disabled]="true">Submit</button>
138+
`,
139+
errors: [{ messageId: Message.USE_DSBTN_DISABLED }],
140+
output: `
141+
<button [dsBtnDisabled]="true">Submit</button>
142+
`,
143+
},
144+
],
145+
} as NamedTests;
146+
147+
export default rule;

0 commit comments

Comments
 (0)